Uboot的start.s源码分析

/*

 *  armboot - Startup Code for ARM920 CPU-core

 *

 *  Copyright (c) 2001 Marius Gr鰃er <mag@sysgo.de>

 *  Copyright (c) 2002 Alex Z黳ke <azu@sysgo.de>

 *  Copyright (c) 2002 Gary Jennejohn <gj@denx.de>

 *

 * See file CREDITS for list of people who contributed to this

 * project.

 *

 * This program is free software; you can redistribute it and/or

 * modify it under the terms of the GNU General Public License as

 * published by the Free Software Foundation; either version 2 of

 * the License, or (at your option) any later version.

 *

 * This program is distributed in the hope that it will be useful,

 * but WITHOUT ANY WARRANTY; without even the implied warranty of

 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

 * GNU General Public License for more details.

 *

 * You should have received a copy of the GNU General Public License

 * along with this program; if not, write to the Free Software

 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,

 * MA 02111-1307 USA

 */

#include <config.h>

#include <version.h>

/*

 *************************************************************************

 *

 * Jump vector table as in table 3.1 in [1]

start.s代码,是u-boot的stage1阶段所要运行的代码

由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM(flash)的0x地址。因此,必须通知编译器以使其知道这个入口,该工作可以通过修改连接器的脚本来完成。

  1. board/smdk2410/uboot.lds:  ENTRY(_start)==>cpu/arm920t/start.o(.text)
  2. Uboot在ram的代码区(TEXT_BASE=0X33F80000)定义在board/amdk2410/config.mk

***********************************************************************

 */

/*

.globl symbol

定义一个全局符号,让这个符号对链接器可见,通常为连接器(ld)使用。symbol是全局可见的,可以供其他链接对象模块使用。

.global _start 让 _start 符号成为可见的标识符,这样链接器就知道跳转到程序中的什么地方并开始执行。linux寻找这个 _start 标签作为程序的默认进入点。标号_start是GNU链接器用来指定第一个要执行指令所必须的,同样的是全局可见的(并且只能出现在一个模块中)。

标号是后边紧跟一个冒号的符号,此时该标号代表活动位置计数器的当前值,并且可作为指令的操作数使用。
*/

.globl _start

_start: b       reset                 /*reset异常不返回,下边都是异常跳转地址*/

ldr pc, _undefined_instruction

ldr pc, _software_interrupt

ldr pc, _prefetch_abort

ldr pc, _data_abort

ldr pc, _not_used

ldr pc, _irq

ldr pc, _fiq

/*

.globl _start 、 _start: 这些伪指令,标号在编译后的代码中并不占用内存。

Ldr 加载指定存储器地址中的内容到寄存器,区分:ldr伪指令

当发生异常时,执行cpu/arm920t/interrupts.c中的中断处理函数

.word expressions :定义一个字,并为之分配空间,4bytes。同理有:

.short expressions/.int expressions/.byte expressions/.long expressions/.ascii "string"

原理分析:在这里先预留好空间,空间里边的内容就是后边的expression,这里expression是标号,则代表的是某个地址,该地址就是异常处理函数的首地址。

当发生某异常时,处理器自动跳到存储器起始的对应异常处理地址(由硬件确定地址,即地址的前几个字,见上代码),执行里边的代码,里边一般都放置一个跳转指令。如:ldr pc, _undefined_instruction ;将此标号地址处的内容取出来给pc指针,而这内容恰好是异常处理函数的首地址(预留的空间就是为了放异常处理地址用的,这里_software_interrupt: .word software_interrupt指令中software_interrupt,是一个标号,标号代表的是处理地址,也是跳转的目标地址,该标号在本文件最下边有定义,其他标号类似),这样完成了一次异常处理。

见图示更易理解:

  地址      地址中内容     指令内容

*/

_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

.balignl 16,0xdeadbeef

/*

这是一个数据对齐指令,在这个.balignl 16,0xdeadbeef指令之前,一共占了4x15=60个字节的内存,15个字。所以本代码的作者当时就简单的在15这个数上,加了个1,即16,把当前指针往后移到地址为64的位置,然后在前面插上了0xdeadbeef这个特殊的值。见图示中0x0000003c位置的内容。详细解释见《于关u-boot中的.balignl 16,0xdeadbeef的理解》一文。

*/

/*

 *************************************************************************

 *

 * Startup Code (reset vector)

 *

 * do important init only if we don't start from memory!

 * relocate armboot to ram

 * setup stack

 * jump to second stage

 *

 *************************************************************************

 */

_TEXT_BASE:

.word TEXT_BASE

.globl _armboot_start

_armboot_start:

.word _start

/*

 * These are defined in the board-specific linker script.

 */

.globl _bss_start

_bss_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 reset code

 */

reset:

/*

 * set the cpu to SVC32 mode

刚进入系统时,设置为管理模式

CPSR_c或SPSR_c的位示意图(控制位:低8位):

7  6  5   4   3   2   1   0

I  F  T  M4  M3  M2  M1  M0

I:IRQ禁止,1禁止,0允许

F:FIQ禁止,1禁止,0允许

T:状态位,0-ARM,1-Thumb

M4-M0:模式位

10000-用户,10001-快速中断,10010-中断,10011-管理,10111-未定义,11111-系统

 */

mrs r0,cpsr

bic r0,r0,#0x1f

orr r0,r0,#0xd3

msr cpsr,r0

/* turn off the watchdog */

/*这些寄存器的地址,根据不同的芯片有不同配置,具体用到时,要更改*/

#if defined(CONFIG_S3C2400)

# define pWTCON 0x15300000

# define INTMSK 0x14400008 /* Interupt-Controller base addresses */

# define CLKDIVN 0x14800014 /* clock divisor register */

#elif defined(CONFIG_S3C2410) /*这是S3C2410的寄存器地址*/

# define pWTCON 0x53000000

# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */

# define INTSUBMSK 0x4A00001C

# define CLKDIVN 0x4C000014 /* clock divisor register */

#endif

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

ldr     r0, =pWTCON     /*这是个ldr伪指令,将pWTCON所代表的32值给r0寄存器*/

mov     r1, #0x0

str     r1, [r0]

/*

* mask all IRQs by setting all bits in the INTMR - default

*/

ldr r0, =INTMSK

mov r1, #0xffffffff

str r1, [r0]

# if defined(CONFIG_S3C2410)

ldr r1, =0x3ff

ldr r0, =INTSUBMSK

str r1, [r0]

# endif  /*CONFIG_S3C2410*/

/* FCLK:HCLK:PCLK = 1:2:4 */

/* default FCLK is 120 MHz ! */

ldr r0, =CLKDIVN

mov r1, #3

str r1, [r0]

#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */

/*

 * we do sys-critical inits only at reboot,

 * not when booting from ram!

 */

/*

cpu_init_crit代码的主要工作是:禁止MMU,以及指令cache和数据cache,因为uboot在开始时,需要用到物理地址,不能是虚拟地址。故禁止MMU.关闭cache,是为了uboot在写入数据的时候,一定要实实在在的写到物理地址上,而不是写到缓存中。顺便再跳转到lowlevel_init.s中,进行sdram的初始化,为以后拷贝代码做好准备

*/

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

bl cpu_init_crit

#endif

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

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

/*

bss段的开始地址,就是代码段的结束地址:_bss_start-_armboot_start=size of data

关于_start 和_TEXT_BASE之间的关系,还有虚拟地址,物理地址在此处的分配,还有待以后研究。

注意:此处的源代码,并没有涉及从nand flash 拷贝数据到ram中,如果需要移植的话,要自己写nand flash的驱动函数,并且拷贝数据到ram中。

*/

ldr r2, _armboot_start

ldr r3, _bss_start

sub r2, r3, r2 /* r2 <- size of armboot            */

add r2, r0, r2 /* r2 <- source end address         */

/*

这个循环拷贝,是从nor flash 中拷贝数据到ram中,并不是从nand flash拷贝数据,nor flash的操作类似于ram,直接操作地址总线,数据总线即可。Nand flash却不可以,需要一定的操作时序和驱动。直白一点:nor flash类似于ram,可以直接跟cpu的数据、控制、地址总线连接,进行控制;nand flash 不可以,有时序限制,需要驱动。

*/

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

#endif /* CONFIG_SKIP_RELOCATE_UBOOT */

/*

 Set up the stack

分析:建立栈空间的过程

Ram中的地址分布(高->低):...,bss区,u-boot镜像,CFG_MALLOC_LEN,GBL_DATA_SIZE,

IRQ&FIQ栈区(可选,视具体情况),用户栈区(向下生长,sp指针初始位置),SDRAM_BASE。_TEXT_BASE并不是ram中的起始地址,只是u-boot放置的首地址,源码中是:0x33f80000。

    */

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    */

/*

借此详细分析:编译器跟代码的交互原理

在ads编译程序代码的时候,会要求我们输入程序的入口代码地址,开始执行地址,各个段的存放首地址(基本由编译器自己根据代码大小情况决定,但我们可以自己指定,虽然一般不这么做)等信息。

类似,在用交叉编译器,编译我们的bootloader代码时,它们也会用到这些地址。不过一般由编译器自己指定一部分(如:__bss_start),我们指定一部分必要的(如:TEXT_BASE,一般放在某些配置文件里边,供编译器查看)。

我们看看由编译器自己指定的情况:

.globl _bss_start

_bss_start:

.word __bss_start

定义一个全局标号_bss_start,标号代表该地址,在该地址处定义了一个字大小的变量,变量内容是__bss_start,而这个__bss_start是由编译器在编译代码时确定的,由编译器把该变量的值填入此位置,供其他代码使用。

__bss_start:应该是编译器内部定义好的变量,专用的。

*/

clear_bss:

ldr r0, _bss_start /* find start of bss segment        */

ldr r1, _bss_end /* stop here                        */

mov r2, #0x00000000 /* clear                            */

clbss_l:str r2, [r0] /* clear loop...                    */

add r0, r0, #4

cmp r0, r1

ble clbss_l

/*这一段代码默认是不编译的,也就是寄存器设置,和时钟设置,参考datasheet就可以*/

#if 0

/* try doing this stuff after the relocation */

ldr     r0, =pWTCON

mov     r1, #0x0

str     r1, [r0]

/*

 * mask all IRQs by setting all bits in the INTMR - default

 */

mov r1, #0xffffffff

ldr r0, =INTMR

str r1, [r0]

/* FCLK:HCLK:PCLK = 1:2:4 */

/* default FCLK is 120 MHz ! */

ldr r0, =CLKDIVN

mov r1, #3

str r1, [r0]

/* END stuff after relocation */

#endif

ldr pc, _start_armboot

_start_armboot: .word start_armboot

/*

 *************************************************************************

 *

 * CPU_init_critical registers

 *

 * setup important registers

 * setup memory timing

 *

 *************************************************************************

 */

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

cpu_init_crit:

/*

以下指令时对协处理器进行操作:cp15

 * flush v4 I/D caches

mcr p15, 0, r0, c7, c7, 0;这一句的意思是:使指令cache和数据cache失效,即:关闭

这一句的具体含义见《arm920t technical reference manual》手册,里边也并未给出具体含义,只是写出了某些操作时需要用到的命令。

mcr p15, 0, r0, c8, c7, 0;关闭指令tlb和数据tlb,文档也只是列出了五个相关指令。

 */

mov r0, #0

mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */

mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */

/*

 * disable MMU stuff and caches

协处理器中register 1是control register

13:V bit  function:base location of exception registers(异常寄存器的基地址)

          0=low address=0x00000000;1=high address=0xffff0000

12: I bit Icache enable ,0=Icache disabled,1=Icache enabled

 9:R bit  ROM protection,this bit modifies the MMU protection system

 8: S bit system protection ,this bit modifies the MMU protection system

 7: B bit endianness ,0=little-endian operation  1=big-endian operation

 2: C bit Dcache enable ,0=Dcache disabled,1=Dcache enabled

 1: A bit Alignment fault enable,Data address alignment fault checking

0=fault checking disabled, 1=fault checking enabled

 0: M bit MMU enable  0=MMU disabled,1=MMU enabled

 */

mrc p15, 0, r0, c1, c0, 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 1 (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.

 */

/*

 r12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复 r12。

ip就是r12寄存器的别名,arm和thumb子程序调用使用规则ATPCS,定义了这些内容。

Reg    APCS   意义
R0      a1   工作寄存器
R1     a2 "
R2   a3 "
R3 a4 "
R4  v1 必须保护
R5 v2 "
R6 v3 "
R7 v4 "
R8 v5 "
R9 v6 "
R10  sl 栈限制
R11  fp 桢指针
R12  ip   
R13  sp  栈指针
R14  lr 连接寄存器
R15  pc 程序计数器

译注:ip 是指令指针的简写。

注意:千万不要把ip和pc两个寄存器混淆了,ip=r12,pc=r15

*/

mov ip, lr //保存子程序返回地址,放到r12中。

bl lowlevel_init //初始化内存配置

mov lr, ip

mov pc, lr

#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

/*

 *************************************************************************

 *

 * Interrupt handling

 *从这里往下,是中断处理函数的调用,一般在uboot中,不会运行到此,uboot中禁用了irq和fiq中断。其他中断应该可以,但一般不会进入。

在此,因为还对编译器中组织的代码不太熟悉,能看懂基本功能即可,不深入追究。等以后再来深入分析原理。

 *************************************************************************

 */

@

@ 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/518601.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SoundTouch对音频处理(Android)

SoundTouch对音频处理&#xff08;Android&#xff09; SoundTouch介绍 SoundTouch 是一个用于音频处理的开源库&#xff0c;主要用于改变音频的速度、音调和音量等特征。您可以在项目中使用 SoundTouch 库来实现音频处理的功能&#xff0c;比如变速播放、音高变化、混响效果…

STM32CubeIDE基础学习-STM32CubeIDE软件快捷键介绍

STM32CubeIDE基础学习-STM32CubeIDE软件快捷键介绍 文章目录 STM32CubeIDE基础学习-STM32CubeIDE软件快捷键介绍前言第1章 查看快捷键方法第2章 设置快捷键方法第3章 常用快捷键示例总结 前言 这个STM32CubeIDE软件使用的是Eclipse框架的开发环境&#xff0c;所以所使用的快捷…

Spring Boot 3核心技术与最佳实践

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 highlight: a11y-dark 引言 Spring Boot作为…

自学新标日第七课(未完结)

第七课 单词 单词假名声调词义コーヒーコーひー&#xff13;咖啡コーラーコーラー&#xff11;可乐お茶おちゃ&#xff10;茶ワインわいん&#xff11;葡萄酒パンぱん&#xff11;面包ケーキけーき&#xff11;蛋糕お粥おかゆ&#xff10;粥昼ごはんひるごはん&#xff13;午…

离散数学——(1)数理逻辑,命题的概念,命题联结词,条件联结词,命题公式,命题的翻译

目录 1.数理逻辑 2.命题的概念 1.命题的判断 ​2.命题的真值 ​ 3.原子命题和复合命题 3.命题联结词 1.否定联结词 2.合取联结词 3.析取联结词 4.条件联结词 ​1.双条件联结词 5.命题公式 1.合式公式 6.命题的翻译 1.数理逻辑 2.命题的概念 1.命题的判断 2.命…

泽众云真机-为什么老机型专区大部分是维护中?如何解决

最近&#xff0c;泽众云真机平台有几位用户向我们咨询&#xff0c;为什么老机型专区大部分是维护中&#xff1f;我想使用这些机型怎么办&#xff1f; 首先来解释一下&#xff0c;为什么叫“老机型专区”&#xff1f;因为一些老的机型之前一直是在线状态&#xff0c;通过我们长期…

springboot245科研项目验收管理系统

科研项目验收管理系统 摘 要 使用旧方法对科研项目信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在科研项目信息的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。这次…

nginx代理minio客户端

错误方式 在点击桶名查看文件时, 会一直处于loading加载中 worker_processes 1; #设置 Nginx 启动的工作进程数为 1。events {worker_connections 1024; ##设置每个工作进程的最大并发连接数为 1024。 }http {include mime.types; #该文件定义了文件扩展名和 MIME 类型…

掌握Nodejs高级图片压缩技巧提升web优化

掌握Nodejs高级图片压缩技巧提升web优化 在当今的数字时代,图像在网络开发中发挥着至关重要的作用。它们增强视觉吸引力、传达信息并吸引用户。然而,高质量的图像通常有一个显着的缺点——较大的文件大小会减慢网页加载时间。为了应对这一挑战并确保快速加载网站,掌握 Node…

friend(c++ 关键字)

定义 C中&#xff0c;friend关键字用于声明友元函数或友元类&#xff0c;它们可以访问类的私有&#xff08;private&#xff09;和保护&#xff08;protected&#xff09;成员&#xff0c;即使它们不是类的成员。这提供了一种突破数据封装和隐藏的方式&#xff0c;使得某些函数…

滞销番茄变废为宝,成功打造市场份额第一番茄品牌|日本极致产品力

可果美是日本市场领先的番茄品牌&#xff0c;然而在可果美发展初期&#xff0c;番茄只是供应链中的滞销产品。可果美通过极致产品力的打造&#xff0c;将番茄“变废为宝”&#xff0c;打败亨氏、味好美成为可果美的招牌。可果美是如何做到的呢? 番茄是可果美供应链的滞销品 在…

多媒体信息处理-重点知识-3. Feature Indexing and Retrieval

Chap 3. Feature Indexing and Retrieval 什么是索引&#xff1f; 为了提高数据集的检索效率而生成的结构化信息 基于特征的相似度匹配是多媒体数据检索方法的基础 从多媒体对象中提取重要特征&#xff0c;将其转化成高维特征向量存储在数据库中 相似性度量&#xff1a; 两种…