vivi源代码分析3

Vivi USB固件加载器:S3C2410平台的固件更新革命 本文还有配套的精品资源,点击获取 简介:Vivi是一个开源固件加载器,专为三星S3C2410处理器设计,通过USB接口简化了嵌入式设备固件的更新和调试流程。它通过S3C2410的USB主机模式,让开发者能够快速便捷地更新固件,提高了开发效率。Vivi的实现涉及USB通信协议、固件加载、中断处理、安全机制和引导流程等关键技术点。开发者可以通过分析vivi-usb压缩包中的源代... 阅读详情
继续分析vivi源代码。
 
step 5:
 
    MTD设备初始化。
 
    关于什么是MTD,为什么要使用MTD,MTD技术的架构是什么,等等,可以参考《Linux MTD源代码分析》(作者:Jim Zeus,2002-04-29)。这份文档的参考价值比较大,猜想作者在当时可能研究了很长时间,毕竟2002年的时候资料还比较缺乏。当然,因为完全分析透彻,方方面面都点透,这份文档还是没有做到。
 
    vivi采用Linux kernel的架构,所以把Linux kernel的MTD子系统借用过来了,做了一些裁减。可以简单地看成:Flash硬件驱动层和MTD设备层。这样,最终以抽象的统一的接口向vivi提供。
 
    还是以nand flash启动这个情景为主线,对MTD初始化流程进行分析。下面先从入口开始。
 

ret = mtd_dev_init();

 
    利用source insight跟踪,看一下此函数的接口定义部分:
 

/*
 * VIVI Interfaces
 */

#ifdef CONFIG_MTD
int write_to_flash(loff_t ofs, size_t len, const u_char *buf, int flag);
int mtd_dev_init(void);
#else
#define write_to_flash(a, b, c, d) (int)(1)
#define mtd_dev_init() (int)(1)
#endif

 
    可见,vivi在配置的时候是必须配置MTD功能部分的。如果不配置MTD,那么CONFIG_MTD就不存在定义。由此导致写flash的动作实际上是没有的。也就是说,无法完成写flash的动作。当然,在这里可以做测试,就是使用MTD子系统的vivi把分区等都设置好。然后重新编译一下vivi,把mtd功能去除,做简单的修改(把bon_cmd部分从【lib/command.c】中去掉,否则编译不通过),生成大小为35152字节。给开发板重新上电,利用老的vivi烧写nand flash的vivi分区,完成后做一下reset,于是没有MTD功能的vivi就跑起来了。但是,这样的bootloader仅仅适合于最终的产品阶段,不适合开发,没什么太大的价值。有兴趣倒是可以据此研究一下配置部分,整个引导时间相应的缩短。我的最小配置文件如下:
 
文件:config.rar
大小:0KB
下载:下载
 
    下面【drivers/mtd/mtdcore.c】,看看mtd_dev_init函数,核心部分就是调用mtd_init函数(【drivers/mtd/maps/s3c2410_flash.c】)。
 

int mtd_init(void)
{
    int ret;

#ifdef CONFIG_MTD_CFI
    ret = cfi_init();
#endif
#ifdef CONFIG_MTD_SMC
    ret = smc_init();
#endif
#ifdef CONFIG_S3C2410_AMD_BOOT
    ret = amd_init();
#endif

 
    可见,vivi现在支持三种类型的存储接口,一种是CFI,也就是Intel发起的一个flash的接口标准,主要就是intel的nor flash系列;一种是smc,智能卡系列接口,nand flash就是通过这个接口实现读写的;一种是AMD的flash系列。选择什么启动方式,就要选择相应的配置项。
 
    核心部分根据配置应该调用smc_init函数。-->【drivers/mtd/maps/s3c2410_flash.c】。这里最为核心的就是两个数据结构,一个是mtd_info,位于【include/mtd/mtd.h】,如下:
 
    mtd_info是表示MTD设备的结构,每个分区也被表示为一个mtd_info,如果有两个MTD设备,每个设备有三个分区,那么在系统中就一共有6个mtd_info结构。关于mtd_info,在《Linux MTD源代码分析》中讲解非常透彻,不过需要注意的是,在vivi的实现中没有使用mtd_table,另外priv指向的是nand_info,这些都是与Linux下不同的地方,主要是为了简化。另一个是nand_info,这个结构则包含了nand flash的所有信息。
 
    所谓的初始化,其实就是填充处理上述两个数据结构的过程。填充完毕之后,后续的工作都会基于此展开。下面开始看smc_init的代码。
 

mymtd = mmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip));
this = (struct nand_chip *)(&mymtd[1]);

 
    在这里,第一句参考前面heap的实现代码,重点看第二句代码。这句代码是有一定的技巧性,但是也存在着很大的风险。其中,mymtd是指向struct mtd_info的指针,那么mymtd[1]实际上是等效于*(mymtd + 1)的数学计算模式,注意mymtd并非数组,这里仅仅利用了编译器翻译的特点。对于指针而言,加1实际上增加的指针对应类型的值,在这里地址实际上增加了sizeof(struct mtd_info),因为前面分配了两块连续的地址空间,所以&(*(mymtd + 1))实际上就是mtd_info数据结构结束的下一个地址,然后实现强制转换,于是this就成为了nand_chip的入口指针了。但是,这里必须要把握好,因为这个地方是不会进行内存的检查的,也就是说,如果你使用了mymtd[2],那么仍然按照上述公式解析,虽然可以运算,可是就是明显的指针泄漏了,可能会出现意料不到的结果。写了一个测试程序,对这点进行了探讨,要小心内存问题。
 
文件:array_test.tar.gz
大小:0KB
下载:下载
 
    了解清楚了,mymtd指向mtd_info的入口,this指向nand_chip的入口。
 

    memset((char *)mymtd, 0, sizeof(struct mtd_info));
    memset((char *)this, 0, sizeof(struct nand_chip));

 

mymtd->priv = this;

 
    上述代码首先初始化这两个结构体,即均为0.然后利用priv把二者联系起来,也就是mymtd通过其成员priv指向this,那么mymtd中的抽闲操作函数,比如read、write等,真正的是通过this来实现的。很明显,this的实现部分属于flash硬件驱动层,而mymtd部分则属于MTD设备层,二者的联系就是通过成员priv实现的。
 
    接下来首先是初始化nand flash设备,这跟前面的基础实验一致。
 

    /* set NAND Flash controller */
    nfconf = NFCONF;
    /* NAND Flash controller enable */
    nfconf |= NFCONF_FCTRL_EN;

    /* Set flash memory timing */
    nfconf &= ~NFCONF_TWRPH1;     /* 0x0 */
    nfconf |= NFCONF_TWRPH0_3;    /* 0x3 */
    nfconf &= ~NFCONF_TACLS;      /* 0x0 */

    NFCONF = nfconf;

 
    然后填充nand flash的数据结构的一个实例this,分成了两个部分,nand flash基本操作函数成员的初始化、其余信息的填写。
 

    /* Set address of NAND IO lines */
    this->hwcontrol = smc_hwcontrol;
    this->write_cmd = write_cmd;
    this->write_addr = write_addr;
    this->read_data = read_data;
    this->write_data = write_data;
    this->wait_for_ready = wait_for_ready;

    /* Chip Enable -> RESET -> Wait for Ready -> Chip Disable */
    this->hwcontrol(NAND_CTL_SETNCE);
    this->write_cmd(NAND_CMD_RESET);
    this->wait_for_ready();
    this->hwcontrol(NAND_CTL_CLRNCE);

    smc_insert(this);

 
    上面这些都不难理解,感觉在结构体设计上还是比较出色的,把成员和相应的操作封装起来,面向对象的一种方法。下面看smc_insert,无非还是按照结构体填写相应的信息,细节部分就不深入探讨了。
 


inline int
smc_insert(struct nand_chip *this) {
    /* Scan to find existance of the device */
    if (smc_scan(mymtd)) {
        return -ENXIO;
    }
    /* Allocate memory for internal data buffer */
    this->data_buf = mmalloc(sizeof(u_char) *
             (mymtd->oobblock + mymtd->oobsize));

    if (!this->data_buf) {
        printk("Unable to allocate NAND data buffer for S3C2410./n");
        this->data_buf = NULL;
        return -ENOMEM;
    }

    return 0;
}

 
    第一部分扫描填充mymtd数据结构。后面主要用于nand flash的oob缓冲处理。具体部分可以参考《s3c2410完全开发》。
 
    这里重点是学习一种结构体的构造技巧。
 
    首先构造一级数据结构,表示抽象实体。例如:
 

struct nand_flash_dev {
    char * name;
    int manufacture_id;
    int model_id;
    int chipshift;
    char page256;
    char pageadrlen;
    unsigned long erasesize;
};

 
    然后构造实例集合,表现形式就是一个大的数组。
 

static struct nand_flash_dev nand_flash_ids[] = {
    {"Toshiba TC5816BDC", NAND_MFR_TOSHIBA, 0x64, 21, 1, 2, 0x1000},    
// 2Mb 5V

    ... ....

    {"Samsung K9D1G08V0M", NAND_MFR_SAMSUNG, 0x79, 27, 0, 3, 0x4000},    // 128Mb

    {NULL,}
};

   
    这样修改扩展等等后续的操作就简便多了。抽象的能力及其训练在读代码的时候是可以很好的学习的,在vivi中,多处都采用了这种设计原则,应该掌握并利用。
 
step 6:
 
    此部分的功能是把vivi可能用到的所有私有参数都放在预先规划的内存区域,大小为48K,基地址为0x
33df0000。在内存的分配示意图方面,《s3c2410完全开发》已经比较详尽,就不放在这里了。到此为止,vivi作为bootloader的三大核心任务:initialise various devices, and eventually call the Linux kernel,passing information to the kernel.,现在只是完成第一方面的工作,设备初始化基本完成,实际上step 6是为启动Linux内核和传递参数做准备的,把vivi的私有信息,内核启动参数,mtd分区信息等都放到特定的内存区域,等待后面两个重要工作使用(在step 8完成,后面的step 7也是为step 8服务的)。这48K区域分为三个组成部分:MTD参数、vivi parameter、Linux启动命令。每块的具体内容框架一致,以 vivi param tlb这个情景为主线进行分析:
 
入口:
 

init_priv_data();

 
进入【lib/priv_data/rw.c】--init_priv_data()
 

int
init_priv_data(void)
{
    int ret_def;
#ifdef CONFIG_PARSE_PRIV_DATA
    int ret_saved;
#endif
    ret_def = get_default_priv_data();
#ifdef CONFIG_PARSE_PRIV_DATA
    ret_saved = load_saved_priv_data();
    if (ret_def && ret_saved) {
        printk("Could not found vivi parameters./n");
        return -1;
    } else if (ret_saved && !ret_def) {
        printk("Could not found stored vivi parameters.");
        printk(" Use default vivi parameters./n");
    } else {
        printk("Found saved vivi parameters./n");
    }
#else
    if (ret_def) {
        printk("Could not found vivi parameters/n");
        return -1;
    } else {
        printk("Found default vivi parameters/n");
    }
#endif
    return 0;

 
    下面分为两步:首先读取默认设置到特定的内存区域,然后读取nand flash的param区域的信息,如果读取成功,就覆盖掉前面的默认设置。首先看第一步,get_default_priv_data--get_default_param_tlb-->
 

int get_default_param_tlb(void)
{
    char *src = (char *)&default_vivi_parameters;
    char *dst = (char *)(VIVI_PRIV_RAM_BASE + PARAMETER_TLB_OFFSET);
    int num = default_nb_params;

    if (src == NULL) return -1;

    /*printk("number of vivi parameters = %d/n", num); */
    *(nb_params) = num;

    //参数表的长度不可以超过预设内存的大小

    if ((sizeof(vivi_parameter_t)*num) > PARAMETER_TLB_SIZE) {
        printk("Error: too large partition table/n");
        return -1;
    }

    //首先复制magic number
    memcpy(dst, vivi_param_magic, 8);

    //预留下8个字节作为扩展
    dst += 16;

    //复制真正的parameter
    memcpy(dst, src, (sizeof(vivi_parameter_t)*num));
    return 0;
}

 
    内存的入口地址为VIVI_PRIV_RAM_BASE+PARAMETER_TLB_OFFSET,开始的8个字节放magic number,这里vivi定义为“VIVIPARA”,后面空下8个字节,留作扩展,从第17个字节开始放置真正的param。这里用到了多处技巧,第一处就是上面刚刚介绍过的数据结构构造技巧,这里的vivi_parameter_t就是一级数据结构:
 

typedef struct parameter {
    char name[MAX_PARAM_NAME];
    param_value_t value;
    void (*update_func)(param_value_t value);
} vivi_parameter_t;    

 
    利用其构造了默认的成员表:
 

vivi_parameter_t default_vivi_parameters[] = {
    { "mach_type",            MACH_TYPE,    NULL },
    { "media_type",            MT_S3C2410,    NULL },
    { "boot_mem_base",        0x30000000,    NULL },
    { "baudrate",            UART_BAUD_RATE,    NULL },
    { "xmodem_one_nak",        0,        NULL },
    { "xmodem_initial_timeout",    300000,        NULL },
    { "xmodem_timeout",        1000000,    NULL },
    { "ymodem_initial_timeout",    1500000,    NULL },
    { "boot_delay",            0x1000000,    NULL }
};

 
    我们这时就可以很清楚的看到param show列出的配置参数了。
 
    另外一个技巧就是利用宏计算数组长度。
 

int default_nb_params = ARRAY_SIZE(default_vivi_parameters);

 
    其中ARRAY_SIZE为:
 

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

 
    这是从Linux kernel中拿来的,也是值得学习和利用的地方。
 
    在load阶段内无非就是找到param分区,然后根据配置,找到相应的flash硬件驱动(这就是MTD层的作用所在,不过可以看出nand chip的databuf确实没有起到作用,现在也未看出这部分究竟用在何处)。然后就是读操作。当然,读取出来的信息先放到临时缓冲区,判断头部的magic number,如果符合则说明是正确的分区信息,然后把信息从临时缓冲区复制到对应的默认配置区,这样就完成了真正的配置。
 
    其实这个地方可以改进。首先看看param分区是否有合适的分区信息,如果有,直接读取到vivi parameter区域,不需要再读取默认的配置信息;如果没有合适的分区信息,然后读取默认的配置信息。这样在用户修正了分区信息时,不必再读取默认的配置信息,这也算是一处优化。
 
step 7:
 
    调用add_command()函数,增加vivi作为终端时命令的相应处理函数。其实,这种机制还是比较简单的,就是利用了链表。
 
    整个命令处理机制及其初始化的实现是在【lib/command.c】中完成的,包括添加命令、查找命令、执行命令、解析命令行等等。具体的命令函数则在相应的模块里面,这样形成了一个2层的软件架构:顶部管理层+底部执行层。维护的核心就是一个数据结构user_command:
 

typedef struct user_command {
    const char *name;
    void (*cmdfunc)(int argc, const char **);
    struct user_command *next_cmd;
    const char *helpstr;
} user_command_t;

 
    第一个成员是指向name字符串的指针,第二个成员就是命令的处理函数,第三个成员是指向下一个命令,第四个成员是帮助信息。如果你想添加一个命令,那么首先需要构造一个数据结构user_command的实例,比如:
 

user_command_t help_cmd = {
    "help",
    command_help,
    NULL,
    "help [{cmds}] /t/t/t-- Help about help?"
};

 
    然后实现命令的真正处理函数command_help。
 

void command_help(int argc, const char **argv)
{
    user_command_t *curr;

    /* help <command>. invoke <command> with 'help' as an argument */
    if (argc == 2) {
        if (strncmp(argv[1], "help", strlen(argv[1])) == 0) {
            printk("Are you kidding?/n");
            return;
        }
        argv[0] = argv[1];
        argv[1] = "help";
        execcmd(argc, argv);
        return;
    }

    printk("Usage:/n");
    curr = head_cmd;
    while(curr != NULL) {
        printk(" %s/n", curr->helpstr);
        curr = curr->next_cmd;
    }
}

 
    构造好之后,需要把它加入链表,也就是在init_builtin_cmds中增加add_command(&help_cmd);,其中add_command的实现如下:
 

void add_command(user_command_t *cmd)
{
    if (head_cmd == NULL) {
        head_cmd = tail_cmd = cmd;
    } else {
        tail_cmd->next_cmd = cmd;
        tail_cmd = cmd;
    }
    /*printk("Registered '%s' command/n", cmd->name);*/
}

 
    这样,自己如果增加新的程序,就按照如上的步骤添加即可。
 
    其余具体命令的实现暂时不做解释。
 
step 8:
 
    根据情况,要么进入vivi的命令行交互界面,要么直接启动内核。关于此部分的流程分析,有了前面的基础和经验,是不难理解的。很容易通过vivi的打印信息得知进行到了第几步,《s3c2410完全开发》在过程上讲解的也很清楚。所以不打算具体分析了。现在翻阅网上资料,有一个问题实际上模模糊糊,如下:
 
    vivi作为bootloader的一个重要的功能就是向Linux kernel传递启动参数,这个情景究竟是如何完成的呢?虽然网上讨论很多,但是因为vivi具有一点特殊性,所以使得理解上有一定的困难。现在已经比较清晰了,算是回答网友的一个问题,也算是总结,就bootloader如何于kernel传递参数,作为一个情景进行详尽的分析。事先需要说明的是,我们假定vivi为A,Linux kernel为B,A要传给B东西,这就是一个通信的过程。要想通信,至少我们得有一个约定,那就是协议。现在存在的协议有两种,一种是基于struct param_struct,不过这种因为其局限性即将作废;一种是基于tags技术。基本的情景框架就是A必须按照协议设置好参数,B呢,就需要来读取解析这些参数。它们之间必须配合好,如果配合不好,那么,kernel是无法引导成功的。现在嵌入式系统的移植,很多时候kernel引导不起来,部分原因就直接来自于参数传递问题。但是设计到这个问题,不能不分析Linux kernel的引导过程。现在还不想细致到代码层,只是根据部分代码把Linux kernel启动至获取引导参数的过程从整体上了解清楚,必要的时候辅助相应的代码。这部分内容的详细分析,专门在下篇总结中完成。
 

 
学习总结:
 
    学习一种技术,采用历史的观点是很好的方法。我们现在学习的技术并非最新的理论研究,所以有大量前人的工作经验可以借鉴。站在巨人的肩上,不做无谓的工作,是好的学习方法。我现在的学习观点就是事先要分析阅读前人的相关经验,包括经典书籍、网上资料、网友的经验等等,然后呢,需要对这些知识理解消化,深入,深入再深入,形成自己的认识,转化成自己的经验。正像网友所说,这些都是现成的技术,只要静下心来肯学,就一定能够学好。
 
    另外,一定要多思考,多动手,多给自己提出问题。没有问题说明你根本就没有深入,有问题才能在解决的过程中提升自己!学习首先从整体上把握流程,然后呢,需要具体的细节。只看整体,不看细节,容易“眼高手低”;只看细节,不看整体,容易“只见树木,不见森林”,提高不到一定的层次。
 
    这些都是学习过程中的经验总结。欢迎交流!

 

转自http://blog.chinaunix.net/u/21948/showart.php?id=379460

宇树机器狗go2—slam建图(1)点云格式 上一篇番外文章教大家如何在宇树机器狗go2的gazebo仿真环境中实现简单的导航运动,本期文章会教大家如何让宇树的机器狗go2在仿真环境中进行slam建图时经常会遇到的一些点云格式,在后续的slam建图和slam算法解析的时候会经常与这些点云信息打交道。宇树机器狗go2仿真会作为一个长期项目来进行不定期更新,仿真不只限于在gazebo上实现,后面还会在isaac sim等仿真平台上教大家如何搭建,其目的还是帮助各位都能以较低的成本加入到宇树机器狗go2的学习中。 阅读详情

相关推荐

Sony DPT RP1/CP1破解攻略

前言 自己有一台Sony DPT CP1和一台kindle,两个都已经用了很多年。直到最近发现微信读书上经常可以找到一些免费而且不错的书,所以就萌生了在两台机器上使用微信读书的想法。kindle比较容易实现,直接打开内置浏览器,进入微信读书网页版就行了,而Sony的电纸书则只能通过破解+安装微信读书的方式实现。大概弄了两三个小时吧,也遇到了一些问题,所以这里记录一下整个过程,希望能方便后来的使用者。 首先感谢HappyZ, osc_t5nbj8ds。前者是提供公开破解的方法的大神,后者则是解决了我遇到的问题

weixin_43467572的博客 1万+

百度SEO站群vivi万能小偷站群版3.5程序源码去除授权

emmm,这个程序名称怎么说是小偷站群呢,其实也就是克隆站点。 也可以说镜像站点。程序无任何使用限制,大家自行研究,希望大家对别人站点多一点的Respect(尊重)。 下载地址: http://www.bytepan.com/k9SJ9G8tgYA 程序使用: 3.5更新了后台各种功能… 比如伪静态 伪静态标识符 伪静态后地址前面标识 如:http://baidu.com/html/xxx.php 修改后记得修改伪静态规则里面的标识符 注意:如果不填的话,需要把后台文件夹重命名为@开头,如:@admi.

百度SEO工具 1435

python监听海康威视报警服务器

因为我使用海康威视摄像头是自带热成像的所以事件报警会上报两张图片,一张是RGB图像、另一张是热成像图像,因此使用。拿到的就是第一张RGB图像。可以点击测试,会显示。

qq_57018478的博客 1162

S3C2410启动信息注释(vivi部分)

uclinux的启动主要分为两个阶段:第一部分bootloader启动阶段第二部分linux 内核初始化和启动阶段     第一节:start_kernel     第二节:用户模式( user_mode )开始,start_kernel结束     第三节:加载linux内核完毕,转入cpu_idle进程Specificatio

qsycn的专栏 1199

FFmpeg视频缩放终极指南:从基础命令到保持宽高比的5种实战技巧

本文是FFmpeg视频缩放的终极指南,详细讲解了从基础命令到保持宽高比的5种实战技巧。针对不同平台(如抖音、B站、Instagram)的分辨率要求,提供了具体的参数模板和批量处理脚本,帮助内容创作者高效、精准地调整视频大小,避免画面变形或黑边问题,实现多平台适配。

juice的博客 462

UE5摄像机抖动全攻略:从Perlin噪点到Sequence实战(附参数调优技巧)

本文深入解析UE5摄像机抖动技术,从Perlin噪点算法到CameraShakeBase蓝图应用,再到Sequence高级控制,提供全面的参数调优技巧。涵盖战斗、爆炸等场景的预设参数,帮助开发者实现专业级抖动效果,提升游戏沉浸感。

weixin_30300225的博客 97

【原创】和菜鸟一起学linux: 第一篇:VIVI烧写

作者:WenYeYe 情人节的前一天,拿了奖金后,正式失业了 在休息的这大半个月里,把以前搜集的资料,精心的分类,并细细的浏览一遍, 并把自己觉得重要的内容,认真的做了笔记,以作备用,嘿嘿,没办法总得混口饭吃嘛 总体来说,这大半个月里,自我感觉收获颇多。 以前只忙赶项目,对一些问题也是一知半解的,总觉的功能完成,领导认可就OK了, 事实并非如此,新东家总爱对你的过去刨根问底,若是答非所...

weixin_34122810的博客 191

当勒索软件盯上虚拟化层:从CVE-2025-22225漏洞看ESXi安全加固要点

本文深度剖析了针对VMware ESXi虚拟化层的CVE-2025-22225等漏洞链的攻击原理,揭示了攻击者如何利用VMCI、HGFS组件从虚拟机逃逸至宿主机。文章从实战角度出发,系统性地提出了ESXi安全加固的核心策略,包括禁用非必要服务、强化主机配置、实施网络分段、建立实时监控与自动化补丁管理体系,为企业构建纵深防御体系提供了具体可行的操作指南。

terraform7cloud的博客 857

s3c2410的Bootloader(Vivi)源代码分析

1.1 Vivi简介Vivi 是韩国mizi 公司开发的bootloader, 适用于ARM9处理器。 Vivi有两种工作模式:启动加载模式和下载模式。启动加载模式可以在一段时间后(这个时间可更改)自行启动linux内核,这时vivi的默认模式。在下载模式下,vivi为用户提供一个命令行接口,通过接口可以使用vivi提供的一些命令,见下表:命令功能Load把二进制文件载入Flash或RAMPart操作MTD分区信息。显示、增加、删除、复位、保存MTD分区Param设置参数Boot启动系统Flash管理Fla

YOU_ARE_MY_FAVORITE的专栏 1026

别再算错了!深入SAP FI后台,看懂外币清账时汇兑损益的自动计算逻辑

刚接手一家跨国企业的SAP财务模块优化项目时,我遇到了一个棘手案例:每月末外币清账产生的汇兑损益金额总是与财务团队的预期存在差异。最令人困惑的是,系统似乎"擅自"计算出了1989.11元的损益,而财务人员反复手工验算都无法复现这个数字。这让我意识到,要真正掌握SAP FI的外币处理能力,必须深入理解系统后台的汇兑损益计算逻辑——不是停留在操作层面,而是穿透到算法内核。这个过程的精妙之处在于,损益金额不是简单比较两个时点的市场汇率,而是反映业务执行全周期的汇率影响。:损益金额与手工计算不符。

weixin_32932149的博客 474

NeurIPS‘25新研究!PINN+GNN强强联手,模型精度飙升50%!

物理图神经网络研究取得重大进展!UCLA团队提出的PhysGNN架构在NeurIPS'2025上引发关注,该模型成功融合PINN的物理约束优势与GNN的图结构处理能力,解决了工程仿真中的关键瓶颈。研究显示,PhysGNN在城市热网预测中误差降低42%,电网负荷模拟的物理规则违反率下降68%。同步解析的SyncTREE模型则创新性地将RC树物理特性融入GNN,实现毫秒级IC时序分析;HSPGNN模型通过嵌入偏微分方程,显著提升多变量时间序列填补精度。

m0_73122726的博客 1022

Bootloader(Vivi)源代码分析

Bootloader(Vivi)源代码分析 ----基于S3C2410处理器 作者:forkchen Email:chenjun@bupt-bcnl.com QQ:56155986 编辑整理:马晨元 Email:mcy2000@21cn.com QQ:12282568 目录 1.1 Vivi简介 1.2 vivi的配置与编译 1.2.1 建立交叉开发环境 1.2.2 配置和

风之恋 1055

前端Three.js面试题及参考答案

渲染循环(Render Loop)是 Three.js 中一个非常重要的概念,它是一个不断重复执行的过程,用于更新和渲染 3D 场景。在每次循环中,渲染循环会更新场景中的对象状态,如位置、旋转、缩放等,然后使用渲染器将更新后的场景渲染成 2D 图像显示在屏幕上。通过不断地重复这个过程,就可以实现动画效果。渲染循环的核心作用是确保场景能够实时更新,给用户带来流畅的视觉体验。如果没有渲染循环,场景将是静态的,不会有任何变化。只有通过不断地更新和渲染,才能让场景中的对象动起来,实现各种动画效果。

大模型大数据攻城狮的专栏 2723

Source Insight 3.50.0065使用详解

转自calvinlee1984 Subject:Source Insight3.50.0065使用详解 Date:     21-Oct-2011 By:         Calvinlee1984@163.com   1.       Source Insight介绍 Source Insight是一个面向项目开发的程序编辑器和代码浏览器,它拥有内置的对C

碧海蓝天观潮涌 506

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

VIVI源码分析

1.1 Vivi简介Vivi 是韩国mizi 公司开发的bootloader, 适用于ARM9处理器。Vivi有两种工作模式:启动加载模式和下载模式。启动加载模式可以在一段时间后(这个时间可更改)自行启动linux内核,这时vivi的默认模式。在下载模式下,vivi为用户提供一个命令行接口,通过接口可以使用vivi提供的一些命令,见下表:命令功

ijustdoit168的专栏 920

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

vivi源代码分析1

通过vivi研究bootloader有一段时间了,基本是在与之相关的基础方面做工作,还没有真正深入研究vivi。以后的学习重心就要放到研究vivi源代码上面了。我想,真正细致地弄清楚vivi实现的细节,对C语言水平的提高,对ARM体系结构的认识,对S3C2410的熟悉,对嵌入式bootloader相关技术,都能有很大的好处。学习的进度会慢一些,但是务求深入,并且打好相关的基础。 一、写在前

menuconfig的专栏 2173

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

vivi源代码最为详细分析(三)

step 5:     MTD设备初始化。     关于什么是MTD,为什么要使用MTD,MTD技术的架构是什么,等等,可以参考《Linux MTD源代码分析》(作者:Jim Zeus,2002-04-29)。这份文档的参考价值比较大,猜想作者在当时可能研究了很长时间,毕竟2002年的时候资料还比较缺乏。当然,因为完全分析透彻,方方面面都点透,这份文档还是没有做到。      在分析代码前先介绍一下MTD(Memory Technology Device)相关的技术。在inux系统中,我们通常会用到不同的存

liangkaiming的专栏 4181

vivi源代码最为详细分析(一)

<br />通过vivi研究bootloader有一段时间了,基本是在与之相关的基础方面做工作,还没有真正深入研究vivi。以后的学习重心就要放到研究vivi源代码上面了。我想,真正细致地弄清楚vivi实现的细节,对C语言水平的提高,对ARM体系结构的认识,对S3C2410的熟悉,对嵌入式bootloader相关技术,都能有很大的好处。学习的进度会慢一些,但是务求深入,并且打好相关的基础。 一、写在前面的话<br />    嵌入式系统软件开发主要包括五个方面:bootloader编写(移植)、驱动程序编写

liangkaiming的专栏 8547

vivi移植开发

<br />今天把vivi编译了一下,顺便解决了几个问题。算是初步工作吧,下一步打算读vivi的源代码,然后改造一下,最后争取自己完成一个小的bootloader。 准备工作(这些都是基于EDUKIT-III教学平台,虽然开发工具上有些不同,但是原理和流程上是相同的。相信理解了嵌入式系统开发流程之后,对这些就会有很清晰的认识): 1)下载Nand_Prog.ide.bin到nor flash。这步工作是为烧写nand flash做准备的。为什么不利用仿真器直接烧写nand flash呢?理解了JTAG的原理

liangkaiming的专栏 2443

VIVI讲解与分析

Vivi 是韩国mizi 公司开发的bootloader, 适用于ARM9处理器。 Vivi有两种工作模式:启动加载模式和下载模式。启动加载模式可以在一段时间后(这个时间可更改)自行启动linux内核,这时vivi的默认模式。在下载模式下,vivi为用户提供一个命令行接口,通过接口可以使用vivi提供的一些命令,见下表: 

LEA的专栏 1896

vivi移植

2007-07-21     今天把vivi编译了一下,顺便解决了几个问题。算是初步工作吧,下一步打算读vivi的源代码,然后改造一下,最后争取自己完成一个小的bootloader。 准备工作(这些都是基于EDUKIT-III教学平台,虽然开发工具上有些不同,但是原理和流程上是相同的。相信理解了嵌入式系统开发流程之后,对这些就会有很清晰的认识): 1)下载Nand_Pr

ggy2005的专栏 1522

vivi源代码最为详细分析(二)

vivi源代码最为详细分析(二) 2011年04月12日   现在进入bootloader之vivi分析的第二阶段,这部分使用C语言实现,部分代码采取内嵌汇编的方式。这里需要用到GNU GCC内嵌汇编的知识,这部分基础还没有具备,需要学习。 下面先按照流程进行分析。需要注意的是,此部分内容并非完全按照原版的vivi源代码,而是加入了自己的理解。另外,对非常简单、google出...

ogd539kk 219

vivi分析

通过vivi研究bootloader有一段时间了,基本是在与之相关的基础方面做工作,还没有真正深入研究vivi。以后的学习重心就要放到研究vivi源代码上面了。我想,真正细致地弄清楚vivi实现的细节,对C语言水平的提高,对ARM体系结构的认识,对S3C2410的熟悉,对嵌入式bootloader相关技术,都能有很大的好处。学习的进度会慢一些,但是务求深入,并且打好相关的基础。   一、写在前

zhougx_1122的专栏 594
上一篇: vivi源代码分析2
下一篇: 字节对齐和C/C++函数调用方式学习总结
menuconfig
博客等级 码龄19年 713粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值