OpenOCD 添加 AIR105(下)

前言

  • 1)上一篇文章介绍了 Air105 OpenOCD 驱动编写的成果,这篇文章想着记录一下中间遇到的问题,一是防止和我一样的小白踩坑(你就当真的听),二是成功不吆喝,如锦衣夜行。

  • 2)ROM API 代码:

    // air105_rom_falsh_api @ 0x00008010UL :
    #define ROM_QSPI_Init                   (*((void     (*)(QSPI_InitTypeDef    *))             (*(uint32_t *)0x8010)))
    #define ROM_QSPI_ReadID                 (*((uint32_t (*)(QSPI_CommandTypeDef *))             (*(uint32_t *)0x8014)))
    #define ROM_QSPI_WriteParam             (*((uint8_t  (*)(QSPI_CommandTypeDef *, uint16_t))   (*(uint32_t *)0x8018)))
    #define ROM_QSPI_EraseSector            (*((uint8_t  (*)(QSPI_CommandTypeDef *, uint32_t))   (*(uint32_t *)0x801C)))
    #define ROM_QSPI_EraseChip			    (*((uint8_t  (*)(QSPI_CommandTypeDef *))             (*(uint32_t *)0x8020)))    // This's a reasonable guess.
    #define ROM_QSPI_ProgramPage            (*((uint8_t  (*)(QSPI_CommandTypeDef *, DMA_TypeDef *,   \
                                                            uint32_t, uint32_t, uint8_t *))      (*(uint32_t *)0x8024)))
    #define ROM_QSPI_ReleaseDeepPowerDown   (*((uint8_t  (*)(QSPI_CommandTypeDef *))             (*(uint32_t *)0x802C)))
    
    

1 驱动实现方式的尝试

1.1 第一次尝试

  • 1)刚开始时,根据 Air001 时的经验,想通过 OpenOCD 模拟 QSPI API 的方式实现,结果烧录巨慢(几 KB 大小的文件烧录要一分多钟),且烧录后无法正常启动,放弃(第一次尝试)。
    • (1)现在想来,烧录慢应该是通过 OpenOCD 模拟寄存器操作只适合偶尔简单的操作,如解锁 FLASH,读取 FLASH SIZE 寄存器等操作。而烧录代码涉及到寄存器的频繁操作;
    • (2)无法正常启动应该是第一个扇区中未写入 header 所致。

1.2 第二次尝试

  • 1)既然操作 QSPI 寄存器不行,就考虑 ROM API 调用的方式。本来想参考 “为国产芯片增加OpenOCD Flash驱动----以AIC8800为例” 这篇文章,写完代码测试时发现运行算法的 target_run_algorithm() 函数一直报错。

  • 2)由于此时还不太明白 OpenOCD 驱动运行逻辑,因此让我一度怀疑官方的 ROM API 到底是否可用。(从这里开始走弯路。)

  • 3)上面提到的代码中的主要逻辑:

    struct aic8800_rom_api_call_code_t {
        uint16_t ldrn_r3;	// 0x4b01
        uint16_t blx_r3;	// 0x4798
        uint16_t bkpt;	    // 0xbe00
        uint16_t nop;	    // 0xbf00
        uint32_t api_addr;	// api addr
    };
    
    struct aic8800_rom_api_call_code_t aic8800_rom_api_call_code_example = {
        .ldrn_r3 = 0x4b01,			/* LDR.N R3, [PC, #0x4]*/
        .blx_r3 = 0x4798,			/* BLX R3 */
        .bkpt = 0xbe00,				/* bkpt */
        .nop = 0xbf00,				/* NOP */
        .api_addr = 0x12345678,
    };
    
    .......
    static int aic8800_probe(struct flash_bank *bank)
    {
        ......    
        retval = target_read_buffer(target, AIC8800_ROM_APITBL_BASE, sizeof(rom_api_table), (uint8_t *)rom_api_table);
        if (retval != ERROR_OK)
            return retval;
        
        for (unsigned int i = 0; i < dimof(aic8800_bank->rom_api_call_code); i++) {
            init_rom_api_call_code(&aic8800_bank->rom_api_call_code[i], rom_api_table[i]);
        }
    	......
    }
    
    • 上面通过 target_read_buffer() 函数,从 ROM API 的基地址读取出 ROM API 代码。

1.3 第三次尝试

  • 1)既然需要运行 ROM 代码,那么我是不是可以把官方 AIR105.FLM 中的算法代码(没有源码)提取出来,然后参考 1.2 中的形式实现来运行?(灵感来自 “利用MDK的FLM文件生成通用flash驱动”)

  • 2)说干就干,不过当我终于解析完 AIR105.FLM 文件,驱动写了一半时想到:AIR105.FLM 中的算法是通过 QSPI 实现的,里面有函数多层嵌套,OpenOCD 中该怎么实现呢?难度太大遂放弃,毕竟我是一个小白呢。

  • 3)解析 AIR105.FLM 文件见 “附录1:调用 AIR105.FLM 算法的尝试”。

1.4 第四次尝试

  • 1)再回头去仔细研究上面的大佬代码,这位大佬的情况好像是先从 FLASH 中读取出 ROM API 的代码,然后将代码写入到 SRAM 中运行的。再看一下 Air105 官方的 ROM API 地址间隔 4 个字节,不像是一个函数的大小啊。

  • 2)经过 “文心一言” 的解答,原来这个是函数指针。那我是不是可以把这个指针指向的地方的代码读取出来运行?好像不行,因为我不知道要读取多长的数据才是一个完整的 ROM API 代码。

    #define ROM_QSPI_ReadID (*((uint32_t (*)(QSPI_CommandTypeDef *))(*(uint32_t *)0x8014)))
    
  • 3)不过大佬的文章仍然给了我启发,那就是将一个地址赋值给一个寄存器,然后可以直接调用。可能官方 ROM API 是没问题的,只是我调用的方式有问题?

    LDR.N   R3, [PC,#0x4]   /* 将后面的入口地址载入 R3,已知的 ROM API 最多只需要用到 R0-R2 */
    BLX     R3              /* 调用 ROM API */
    
  • 4)那么就验证一下,当时想到如果有问题就放弃这个项目了(因为在 1.2 小节中,我尝试了各种办法结果都是报错)。通过在 Keil5 中调用 ROM_QSPI_ReadID() 成功获得芯片的 ID,然后通过调试找到对应的汇编代码(这也是我认为 Keil5 唯二可取的地方):

    0x01002424 F44F4000  MOV           r0,#0x8000
    0x01002428 6941      LDR           r1,[r0,#0x14]
    0x0100242A 2000      MOVS          r0,#0x00
    0x0100242C 4788      BLX           r1
    0x0100242E 4604      MOV           r4,r0
    
    • (2)通过 BLX r1 指令来调用 0x8014 处的函数,且参数 NULL 保存在 r0 寄存器中。结果成功获得 id,如下:

    • (3)再结合大佬文章中的 BLX 调用 API 代码,那么可以确定,确实可以这样来调用 ROM API 的函数指针,只要我们把参数设置完就好了(乐极生悲的伏笔)

1.5 第五次尝试

  • 1)当时想着先实现 ROM_QSPI_ReadID() ROM API 的调用,因为它参数少,且是 OpenOCD 烧录时最先调用的 .probe 中需要实现的。当看到程序输出下面的内容时,就差喜极而泣了:

    AIR105 flash base 0x01001000 device id = 0x005E4016
    
  • 2)路线选择完成,下面就把各个功能函数实现即可:

    const struct flash_driver air105_flash = {
        .name = "air105",
        .commands = air105_command_handlers,
        .flash_bank_command = air105_flash_bank_command,
        .erase = air105_erase,
        .write = air105_write,
        .read = default_flash_read,
        .probe = air105_probe,
        .auto_probe = air105_auto_probe,
        .erase_check = default_flash_blank_check,
        .info = air105_get_info,
        .free_driver_priv = default_flash_free_driver_priv,
    };
    
  • 3)其它情况:

    • (1)为了减少烧录时间,当然是采用异步烧录了
    • (2)驱动完成(见 air105.c),汇编算法完成(见 air105_write.S),编写过程中,通过 Keil5 测试 ROM API 时发现:
      • a. 扇区擦除函数 ROM_QSPI_EraseSector(),参数 uint16_t 为扇区所在地址,那么擦除实现时就需要循环擦除多个扇区了,原来还以为 API 擦除的是指定地址到 FLASH 尾地址呢
      • b. 页编程函数:ROM_QSPI_ProgramPage(),芯片的写入单位是页,那么就要除了实现异步烧录算法外,还要实现 ROM_QSPI_ProgramPage() 函数,防止不足页的情况

2 测试

2.1 汇编传参问题

  • 1)通过 “flash write_image erase” 烧录后,程序无法运行,于是通过 “flash read_bank 0 [filename]” 将整个 FLASH 读取出来,与 HEX 文件对比:发现异步烧录算法写入数据不对(而且有时在写的过程中会报错),折腾了几天,中间几乎一行一行地查代码感觉没有问题啊。

  • 2)最终在不经意间愤怒地质问 “文心一言”:“arm 汇编中 5 个参数的函数到底应该怎么传参?(概义如此)”,答:r0~r3 寄存器通常用于参数传递,多于 4 个参数就要放到栈里面去了。对不起,我是小白。下面是 QSPI API(与 ROM API 参数一致),有 5 个参数:

    uint8_t QSPI_ProgramPage(QSPI_CommandTypeDef *cmdParam,
                             DMA_TypeDef *DMA_Channelx,
                             uint32_t adr,
                             uint32_t sz,
                             uint8_t *buf
    );
    
    • (1)ARM 平台下,参数值传递按顺序存放在寄存器 r0,r1,r2,r3 里,超过 4 个参数值传递则放栈里。仔细想想也是,通用寄存器就那十几个,那我要是一个函数 20 个参数怎么办呢?
    • (2)页编程函数 QSPI_ProgramPage() 有 5 个参数,则第 5 个参数需要去 sp 指针指向的位置去取。
  • 3)同时中间想到,由于我们是异步烧录,那么我们需要几个寄存器来保存 FIFO 的起始、结束地址,烧录页数,读写指针等,这些寄存器在烧录过程中不能被外部改变。

    • (1)原来想着我从 R12~R0 开始往下用,BLX 指令调用函数时可能只用到前面的几个寄存器,不会覆盖我后面的寄存器。
    • (2)后来查看 AIR105.FLM 文件,以及《ARM Cortex-M3 权威指南》时发现,POP 和 PUSH 指令你值得拥有。

2.2 缓存

  • 1)每次烧录成功后,通过 flash mdw 查看发现内容不对。

    • mdw,Memory Display Word,显示内存字,即显示指定地址的一个 4 字节数据。
  • 2)然后就一直通过 “flash fillw [address] [value] [length]” 写入数据,“flash erase_sector [bank] [first] [last]” 擦除扇区,“flash erase_check [bank]” 查看扇区的标记情况,这三个命令来测试。

  • 3)偶然的机会发现,写入数据后,使用 “flash erase_check” 走一下,然后再通过 “flash mdw” 命令就可以看到正常值,然后就把 “flash erase_check” 对应的回调函数 “default_flash_blank_check” 添加到每次更新操作的后面,结果非常消耗时间。这样不行啊,再去翻 AIR105.FLM 解析内容,通过 “Cache_CleanAll()” 函数猜测可能是缓存问题。

  • 4)找到官方数据手册,把清除缓存的功能实现一下,再测试,终于正常烧录了。 通过 “flash read_bank 0 [filename]” 命令读取出来后,与镜像 HEX 文件一致。

2.3 header

2.3.1 无法正常启动
  • 1)程序已经烧录成功,校验也和镜像文件一致,但芯片无法正常启动。需要说明的是,此前的驱动中,我一直以为芯片的 FLASH 基地址为 0x01001000,且大小为 4092KB,因为官方文档这样说得,且 Link Script 中也如下指定(虽然心里也吐槽过,说好的 4MB FLASH 怎么少了 4KB 呢?)

    MEMORY
    {
        RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 640K
        FLASH    (rx)    : ORIGIN = 0x01001000,   LENGTH = 4092K
    }
    
  • 2)这里是耗时最长的部分,经过大概一周的折腾,包括一行一行检查汇编代码(怀疑自已),去查 SC300 文档(以为 SC300 安全核有啥特性),查看 Cortex-M3/M4 权威指南(想找找 FLASH 寄存器的说明),所有的路都走不能,再总结一下现象如下:

    • (1)先通过串口烧录一次,然后使用本驱动多次烧录相同的镜像,都可以正常运行
    • (2)通过 flash read_bank 命令读取出烧录的镜像文件,发现和正常运行的镜像一致
2.3.2 串口烧录程序
  • 1)OpenOCD 烧录无法正常运行,但串口烧录后却可以正常运行,观察下串口烧录的日志:

    PS D:\_Workspace\11_MCU\07_AirM2M\Air105\Demos\air105_mh1903s> .\air105-uploader.exe COM17 .\cmake-build-debug\air105_mh1903s.bin 
    >>> No signature key. Ignoring firmware signature
    >>> Port: COM17
    >>> Firmware: 4312 bytes
    >>> Starting bootloader
    >>> Received ChipSN Packet
    >>>  Boot Version: V1.3.0
    >>>  ChipID: 50790400
    >>>  ROM Version: Unknown
    >>>  Series: S030 (MH1903S)
    >>>  Serial: b'4131303551383854000000000000DAA9'
    >>> Initializing stage2
    >>> Erasing flash memory
    >>> Erasing flash from 00000000 to 00003000
    >>> Sending firmware
    >>> Writing @0x1001000
    >>> Writing @0x1002000
    >>> Upload finished
    >>> Resetting device
    
    • 没发现什么特殊的,烧录地址也是在 0x01001000。就死马当做活马医,看看它是怎么实现烧录的。“马生,你发现了华点!!!”
  • 2)发现了如下内容:

    def update(self):
        if self.Option == 2:
            hash = hashlib.sha256(self.Data).digest()
        else:
            hash = hashlib.sha512(self.Data).digest()
    
        self.Hash[:len(hash)] = hash
    
        self.header = struct.pack(
            "<6I", 0x5555AAAA, 0, self.Start, self.Length, self.Version, self.Option)
        self.header += self.Hash
        crc = self.c32.compute(self.header[4:])
        self.header += struct.pack("<I", crc)
    
        if self.rsaKey != NULL_KEY:
            signData = self.header[4:]
            signData += b"\x00" * (256 - len(signData))  # Pad to 256 bytes
            signData = int.from_bytes(signData, byteorder='big')
            signed = pow(signData, self.rsaKey.d, self.rsaKey.n)
    
            self.signed_header = self.header[:4] + signed.to_bytes(
                self.rsaKey.size_in_bytes(), byteorder='big')
    
    • 好像是对数据进行 SHA256 摘要,还有 CRC-32 签名的。
  • 3)与此同时,官方 SDK 里面竟然有如下定义:

    #define MHSCPU_FLASH_BASE                       (0x01000000UL)
    
  • 4)尝试将 OpenOCD 中 Air105 驱动程序中的 FLASH 基地址由 0x01001000 修改为 0x01000000,然后通过 “flash mdw 0x01000000 32” 读取内容,结果:

    > flash mdw 0x01000000 32
    AIR105 flash base 0x00000000 device id = 0x005E4016
    0x01000000: 5555aaaa 00000000 01001000 000010d8 00000000 00000002 5ac6ffb0 b06f56f2
    0x01000020: 756ff9bb 38a5f690 afbaf183 0c26b5cb d8008a24 d76168e3 00000000 00000000
    0x01000040: 00000000 00000000 00000000 00000000 00000000 00000000 2f91401d 00000000
    0x01000060: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    
    • 从中可以发现,虽然后续内容不一致,但开始的 0x0x5555AAAA 与串口烧录代码中的一模一样。
  • 5)再仔细研究一下,终于发现,原来 FLASH 0x01000000 ~ 0x01001000 一个扇区 4KB 大小中,保存的是 0x0x5555AAAA 魔数、默认值0、app 地址、app 数据长度、版本、摘要算法等内容。接下来我们只需要一一实现这些内容即可。

  • 参考:sha256 C 语言实现:https://blog.csdn.net/qq_43176116/article/details/110388321

2.4 烧录 .elf 文件

  • 1)我的目标是在 CLion 中使用 OpenOCD 烧录程序,而 CLion 中烧录的是 .elf 文件。问题是,无论在 Link Script 文件怎么配置 FLASH 的起始地址 ORIGIN,结果在烧录时,OpenOCD 获取到的都是 0x01000000

    MEMORY
    {
        RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 640K
        FLASH    (rx)    : ORIGIN = 0x01001000,   LENGTH = 4092K
    }
    
  • 2)使用 “arm-none-eabi-readelf.exe -a .\air105_mh1903s.elf” 命令解析 .elf 文件,结果也是如此:

    Program Headers:
      Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
      LOAD           0x000000 0x01000000 0x01000000 0x01cac 0x01cac RWE 0x10000
      LOAD           0x008000 0x20008000 0x01001cac 0x0042c 0x0045c RW  0x10000
      LOAD           0x000000 0x20000000 0x20000000 0x00000 0x08000 RW  0x10000    
    
  • 3)晚上睡觉时,找了几篇类似的文件了解一下 Link Script 文件:

    • ARM Cortex-M文件那些事(6)- 可执行文件(.out/.elf):https://www.cnblogs.com/henjay724/p/8276677.html
    • ARM Cortex-M文件那些事(2)- 链接文件(.icf):https://www.cnblogs.com/henjay724/p/8191908.html
    • 链接脚本(Linker Scripts)语法和规则解析:https://www.cnblogs.com/jianhua1992/p/16852784.html
  • 4)早上起来文章还没看,先用 “flash mdw 0x01001000 32” 读取烧录后的头 32 个字节发现,虽然 .elf 文件的内容从地址 0x01000000 开始,但是它实际烧录的程序代码依然是从 0x01001000 开始写入的。那问题不就解决了,我只需要在计算 header 时,对传入 .write 函数中的 buffer 进行 1 个扇区 4KB 的偏移不就行了。

  • 5)结果不出所料,果然烧录成功。至此可以宣布,我的 OpenOCD 驱动终于可以支持 .elf、.hex、.bin 三种文件的烧录了。

3 优化

3.1 DMA

  • 1)可以正常烧录以后,当然就想优化一下烧录速度了。首先从参数类型我们可以看到烧录 ROM API 是支持 DMA 的,那就尝试一下吧。
#define ROM_QSPI_ProgramPage (*((uint8_t (*)(QSPI_CommandTypeDef *, DMA_TypeDef *, uint32_t, uint32_t, uint8_t *))(*(uint32_t *)0x8024)))

#define DMA_Channel_0                           ((DMA_TypeDef *)DMA_BASE)
#define DMA_Channel_1                           ((DMA_TypeDef *)(DMA_BASE + 0x58))
  • 2)从 SDK 中可以看到 DMA 通道号的值,再使用 Keil5 进行 Debug 可以看到在 ROM_QSPI_ProgramPage() 调用时,r1 的值确实是 0x40008000,那么我们修改汇编代码:
    movs    r1, #0x4000     /* param of DMA_TypeDef         */
    lsls    r1, #16
    adds    r1, #0x800      /* DMA always failure           */
    
    // 还尝试了下面这种方式:
    ldr     r1, =#0x4000800
    
    • 还尝试了其它 DMA 通道号,结果发现:有时会出现死活烧录下进行的情况。此时测试不加 DMA 的情况下烧录,只比 Keil 慢 5~6 秒,就放弃了。

3.2 多扇区擦除

  • 1)为了对比 Keil 烧录速度,在擦除扇区和烧录分别加入了计时功能,结果发现擦除 150 个扇区都需要十几秒,就想到设置擦除的最后扇区,让擦除算法自已运行,这样可以节省每次擦除算法的初始化和释放过程时间。

    • 也就是由 OpenOCD 控制一个扇区一个扇区地擦除,修改为几十个扇区擦除一次。
  • 2)结果发现,当一次擦除 50 个以上扇区时,发现错误的概率大大提高。慢慢减少每次擦除的扇区数,直到等于 20 时,还会偶尔出现错误。没办法,设置成每次擦除 15 个扇区,并加入错误重试功能。

        /* erase sectors more than specified sectors will make ROM_QSPI_EraseSector() failed.
         * and there is a certain probability that the function returns successfully, but the
         * sector is not completely erased, this can lead to the program not running after being
         * burned.
         * The smaller the specified number of sectors, the better. it will always failed if more
         * than 60 sectors per erased.
         */
        while (num_sectors > 0) {
            erase_last = erase_first + MIN(num_sectors, MAX_ERASE_SECTORS_ONCE);
            /* Loop over the selected sectors and erase them. */
            retval = ROM_QSPI_EraseSector(bank->target, erase_first, erase_last);
            if (retval != ERROR_OK) {
                LOG_WARNING("AIR105 erase sector from %d to %d error(retry):%d", erase_first, erase_last, retval);
                if (error_times++ > 5) {
                    return retval;
                }
                continue;
            }
            erase_first = erase_last;
            num_sectors -= MAX_ERASE_SECTORS_ONCE;
        }
    
  • 3)至此,AIR105 驱动终于开发完成了。皇天不负有心人,CLion 给的惊喜太大了:

    • 命令行测试时,最高速度只有 40 KiB/s 左右,这一下提高了快 4 倍。

4 附录1:调用 AIR105 FLM 算法的尝试

4.1 关于 Keil FLM

  • 1)首先查看一下 FLASH 相关信息(这里显示了 15 MB 的大小,但手册上说只有 4MB):

  • 2)Keil 规定,一个 FLM 文件中要包含一些指定函数及结构:

    • (1)函数(FlashPrg.c):

      函数名是否必须描述
      Init初始化
      UnInit反初始化
      EraseSector扇擦除
      ProgramPage页擦除
      EraseChip片擦除。
      BlankCheckBlank 检查。
      Verify校验。
    • (2)FLASH 说明结构体(FlashDev.c):

    struct FlashDevice const FlashDevice  =  {
       FLASH_DRV_VERS,             // Driver Version, do not modify!
       "New Device 256kB Flash",   // Device Name 
       ONCHIP,                     // Device Type
       0x00000000,                 // Device Start Address
       0x00040000,                 // Device Size in Bytes (256kB)
       1024,                       // Programming Page Size
       0,                          // Reserved, must be 0
       0xFF,                       // Initial Content of Erased Memory
       100,                        // Program Page Timeout 100 mSec
       3000,                       // Erase Sector Timeout 3000 mSec
     
        // Specify Size and Address of Sectors
       0x002000, 0x000000,         // Sector Size  8kB (8 Sectors)
       0x010000, 0x010000,         // Sector Size 64kB (2 Sectors) 
       0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)
       SECTOR_END
    };
    
  • 3)函数说明:

    /**
     * FLASH 初始化,每次编程时调用
     *
     * Keil 提供的示例代码中实现了三件事:
     * (1)设置 FLASH 读取间隔(LATENCY)
     * (2)解锁 FLASH
     * (3)增加独立看门狗(IWDG)喂狗时间(应该是防止程序烧录过程中喂狗时间到)
     *
     * @param adr   Device base address
     * @param clk   Clock frequency(Hz)
     * @param fnc   Function code: 1, Erase; 2, Program; 3,Verify.
     * @return  status information: 0, on success; 1, on failure.
     */
    int Init (unsigned long adr, unsigned long clk, unsigned long fnc);
    
    /**
     * FLASH 反初始化,在一次擦除、编程、或校验后调用。
     *
     * Keil 提供的示例代码:重新锁定 FLASH
     *
     * @param fnc   Function code: 1, Erase; 2, Program; 3, Verify.
     * @return status information: 0, on success; 1, on failure.
     */
    int UnInit (unsigned long fnc);
    
    /**
     * 扇区擦除
     *
     * Keil 提供的示例代码:重新锁定 FLASH
     *
     * @param addr   Sector address
     * @return status information: 0, on success; 1, on failure.
     */    
    int EraseSector (unsigned long adr);
    
    /**
     * 页编程
     *
     * Keil 提供的示例代码为每次写半个字(即 2 个字节)
     *
     * @param adr	Page start address
     * @param sz	Page size
     * @param buf	Data to be written
     * @return  status information: 0, on success; 1, on failure.
     */
    int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf);
    
    /**
     * 片擦除
     *
     * @return  status information: 0, on success; 1, on failure.
     */
    int EraseChip (void);
    
    /**
     * Blank 检查,检查指定块是否是指定内容 pat。
     *
     * @param adr	Block start address
     * @param sz	Block size in bytes
     * @param pat	Pattern to compare
     * @return  status information: 0, block content == pattern pat; 1, block content != pattern pat.
     */
    int BlankCheck (unsigned long adr, unsigned long sz, unsigned char pat);
    
    /**
     * 数据校验,校验 FLASH 数据是否和 buf 内容一致
     *
     * @param adr	Start address
     * @param sz	Size in bytes
     * @param buf	Data to be compared
     * @return  success, sum of adr+sz; failure, other number represents the failing address.
     */
    unsigned long Verify(unsigned long adr, unsigned long sz, unsigned char *buf);
    

4.2 解析 FLM 文件

  • 1)通过 arm-none-eabi-readelf 命令,找到 FLM 中定义的必要函数所在位置。

    $ arm-none-eabi-readelf.exe --help
    
    Usage: readelf <option(s)> elf-file(s)
    Display information about the contents of ELF format files
    
    $ arm-none-eabi-readelf.exe -a Air105.FLM
    
    Symbol table '.symtab' contains 371 entries:
    Num:    Value  Size Type    Bind   Vis      Ndx Name
    297: 00000089    32 FUNC    GLOBAL HIDDEN     1 BlankCheck
    317: 00000d61    12 FUNC    GLOBAL HIDDEN     1 EraseChip
    318: 00000d6d    40 FUNC    GLOBAL HIDDEN     1 EraseSector
    319: 00000d95   332 FUNC    GLOBAL HIDDEN     1 Init
    320: 00000ffd    86 FUNC    GLOBAL HIDDEN     1 ProgramPage
    365: 00001f89    26 FUNC    GLOBAL HIDDEN     1 UnInit
    
  • 参考:https://blog.csdn.net/sinat_31039061/article/details/128350295

  • 26
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenOCD是一个开源的软件调试和编程工具,它支持许多不同的硬件平台和芯片,包括第三方芯片。为了添加第三方芯片的支持,我们需要做下列工作: 1. 了解芯片的调试接口和协议:首先,我们需要获取第三方芯片的数据手册,了解它的调试接口和通信协议。这些细节通常包括调试端口的引脚定义、通信协议(如JTAG或SWD)和指令集。 2. 修改OpenOCD配置文件:OpenOCD使用配置文件来定义和配置支持的芯片。我们需要编辑配置文件,添加第三方芯片的定义和其他必要的参数。这些参数通常包括芯片的ID代码、调试接口类型、频率和通信速度等。 3. 编译和安装OpenOCD:在修改配置文件后,我们需要重新编译OpenOCD,并将其安装到我们的开发环境中。这通常涉及到使用工具链编译源代码、链接库文件,并将可执行文件复制到适当的目录中。 4. 测试和调试:一旦安装完成,我们可以使用OpenOCD来连接并调试我们的第三方芯片。通过适当的命令和选项,我们可以初始化和配置调试接口,并执行调试操作,如读写寄存器、设置断点和单步调试等。如果有任何问题,我们可以通过查看OpenOCD的输出日志、调试和修改配置文件来解决。 通过这些步骤,我们就能够将一个第三方芯片添加OpenOCD的支持列表中,并使用OpenOCD进行调试和编程操作。这为开发人员提供了更多的灵活性和工具选择,以便在开发过程中更好地支持各种类型的芯片和设备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值