FlashAlgo 算法python实现

news/2024/11/29 12:47:53/

离线编程器:

1、首先,既然是SWD编程器,那首先当然是要实现SWD时序协议了
由于单片机都没有SWD外设,所以只能用GPIO模拟实现SWD时序,,这部分功能已经由ARM公司的CMSIS-DAP代码实现

2、然后就是基于CMSIS-DAP,实现通过DAP读写目标芯片的内存、内核寄存器,,这部分功能已经由DAPLink里面的swd_host.c文件实现

同时,swd_host.c还实现了另一个对实现编程器至关重要的函数:

  1. uint8_t swd_flash_syscall_exec(const program_syscall_t *sysCallParam, uint32_t entry, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4)

复制代码


它的作用是通过DAP在目标芯片上执行

那么,我们只要把编程算法(一段在目标芯片上执行的代码,里面有Flash_Erase、Flash_Write两个函数)通过SWD写入目标芯片的SRAM,然后再通过SWD调用目标芯片SRAM里面的Flash_Erase、Flash_Write两个函数,不就能实现通过SWD给目标芯片编程了吗??

所以,程序的主体结构就是:

其中target_flash_init()的主要作用就是把芯片的编程算法下载到目标芯片的SRAM中去

好了,SWD编程器已经实现



不过还有一个问题:要下载到目标芯片SRAM中去的编程算法从哪里来??

我们知道,Keil针对每一颗芯片都有一个Flash编程算法,这个算法存在一个后缀为.FLM的文件里面,,要是我们能把.FLM文件里面的算法内容抽取出来给我们用,,那不就完美了吗

3、其实这个功能也已经有国外大神给实现了,GitHub上的FlashAlgo项目里面有个flash_algo.py文件,它就是用来实现这个功能的
 

flash_algo.py

#! python3
"""
 mbed
 Copyright (c) 2017-2017 ARM Limited
"""
import os
import io
import struct
import binascii
from collections import namedtuple

import jinja2
from elftools.elf.elffile import ELFFile


class PackFlashAlgo(object):
    REQUIRED_SYMBOLS = (
        "Init",
        "UnInit",
        "EraseSector",
        "ProgramPage",
    )

    EXTRA_SYMBOLS = (
        "BlankCheck",
        "EraseChip",
        "Verify",
        "Read",
    )

    def __init__(self, data):
        ''' 从ELF文件中依次解析出Flash信息和所需的symbols、sections,
            然后根据section的信息创建可在RAM中执行的算法程序的二进制blob '''
        self.elf = ElfFileSimple(data)

        self.flash_info = PackFlashInfo(self.elf)
        self.flash_start     = self.flash_info.start
        self.flash_size      = self.flash_info.size
        self.flash_page_size = self.flash_info.page_size


        self.symbols = {}
        for symbol in self.REQUIRED_SYMBOLS:
            if symbol not in self.elf.symbols: raise Exception("Missing symbol %s" % symbol)
            self.symbols[symbol] = self.elf.symbols[symbol].value
        for symbol in self.EXTRA_SYMBOLS:
            if symbol not in self.elf.symbols: self.symbols[symbol] = 0xFFFFFFFF
            else:                              self.symbols[symbol] = self.elf.symbols[symbol].value


        ''' 算法程序工程的link脚本:
        PRG 0 PI
        {
            PrgCode +0           ; Code
            {
                * (+RO)
            }
            
            PrgData +0           ; Data
            {
                * (+RW,+ZI)
            }
        }
        '''
        ro_rw_zi = [None, None, None]
        for section in self.elf.iter_sections():
            for i, name_and_type in enumerate((("PrgCode", "SHT_PROGBITS"),
                                               ("PrgData", "SHT_PROGBITS"),
                                               ("PrgData", "SHT_NOBITS"),)):
                if name_and_type != (section.name, section["sh_type"]): continue
                if ro_rw_zi[i] is not None: raise Exception("Duplicated section")

                ro_rw_zi[i] = section

        ''' 若zi段丢失,创建一个空的 '''
        s_ro, s_rw, s_zi = ro_rw_zi
        if s_rw is not None and s_zi is None:
            s_zi = {
                "sh_addr": s_rw["sh_addr"] + s_rw["sh_size"],
                "sh_size": 0
            }
        
        if s_ro is None: raise Exception("RO section is missing")
        if s_rw is None: raise Exception("RW section is missing")
        if s_zi is None: raise Exception("ZI section is missing")
        if s_ro["sh_addr"] != 0:
            raise Exception("RO section does not start at address 0")
        if s_ro["sh_addr"] + s_ro["sh_size"] != s_rw["sh_addr"]:
            raise Exception("RW section does not follow RO section")
        if s_rw["sh_addr"] + s_rw["sh_size"] != s_zi["sh_addr"]:
            raise Exception("ZI section does not follow RW section")

        self.ro_start = s_ro["sh_addr"]
        self.ro_size  = s_ro["sh_size"]
        self.rw_start = s_rw["sh_addr"]
        self.rw_size  = s_rw["sh_size"]
        self.zi_start = s_zi["sh_addr"]
        self.zi_size  = s_zi["sh_size"]

        
        algo_size = s_ro["sh_size"] + s_rw["sh_size"] + s_zi["sh_size"]
        self.algo_data = bytearray(algo_size)
        for section in (s_ro, s_rw):
            start = section["sh_addr"]
            size  = section["sh_size"]
            assert len(section.data()) == size
            self.algo_data[start : start+size] = section.data()

    def format_algo_data(self, spaces, group_size, fmt):
        ''' 返回一个能表示algo_data的字符串 suitable for use in a template
            spaces: 每行的前导空格个数    group_size: 每行的元素个数    fmt: hex、c
        '''
        padding = " " * spaces
        if fmt == "hex":
            blob = binascii.b2a_hex(self.algo_data)
            line_list = []
            for i in range(0, len(blob), group_size):
                line_list.append('"' + blob[i:i + group_size] + '"')
            return ("\n" + padding).join(line_list)
        elif fmt == "c":
            blob = self.algo_data[:]
            pad_size = 0 if len(blob) % 4 == 0 else 4 - len(blob) % 4
            blob = blob + b"\x00" * pad_size
            integer_list = struct.unpack("<" + "L" * (len(blob) // 4), blob)
            line_list = []
            for pos in range(0, len(integer_list), group_size):
                group = ["0x%08X" % value for value in
                         integer_list[pos:pos + group_size]]
                line_list.append(", ".join(group))
            return (",\n" + padding).join(line_list)
        else:
            raise Exception("Unsupported format %s" % fmt)

    def process_template(self, template_path, output_path, data_dict=None):
        ''' 用模板文件生成输出文件, 此类的所有数据都可以在模板中通过 'algo' 访问
            data_dict: 生成输出时模板需要的其他数据                        '''
        if data_dict is None: data_dict = {}
        
        assert "algo" not in data_dict, "algo already set by user data"
        data_dict["algo"] = self

        with open(template_path) as f:
            template = jinja2.Template(f.read())

            with open(output_path, "wb") as f:
                f.write(template.render(data_dict).encode('latin'))


class PackFlashInfo(object):
    """从ELF文件中解析出FlashDev.c文件中定义的FlashDevice结构体变量的值"""

    FLASH_DEVICE_STRUCT = "<H128sHLLLLBxxxLL"   # FlashDevice结构体的成员
    FLASH_SECTOR_STRUCT = "<LL"                 # FlashSector结构体的成员
    FLASH_DEVICE_STRUCT_SIZE = struct.calcsize(FLASH_DEVICE_STRUCT)
    FLASH_SECTOR_STRUCT_SIZE = struct.calcsize(FLASH_SECTOR_STRUCT)
    SECTOR_END = (0xFFFFFFFF, 0xFFFFFFFF)

    def __init__(self, elf):
        info = elf.symbols["FlashDevice"]
        info_start = info.value
        info_size  = self.FLASH_DEVICE_STRUCT_SIZE
        info_data  = elf.read(info_start, info_size)

        values = struct.unpack(self.FLASH_DEVICE_STRUCT, info_data)

        self.version          = values[0]
        self.name             = values[1].strip(b"\x00")
        self.type             = values[2]
        self.start            = values[3]
        self.size             = values[4]
        self.page_size        = values[5]
        self.value_empty      = values[7]
        self.prog_timeout_ms  = values[8]
        self.erase_timeout_ms = values[9]

        self.sector_info_list = []
        for i in range(512):    # 最多512个sector
            data = elf.read(info_start + info_size + self.FLASH_SECTOR_STRUCT_SIZE * i, self.FLASH_SECTOR_STRUCT_SIZE)
            size, addr = struct.unpack(self.FLASH_SECTOR_STRUCT, data)

            if (size, addr) == self.SECTOR_END: break

            self.sector_info_list.append((addr, size))

    def __str__(self):
        desc = ""
        desc += "Flash Device:" + os.linesep
        desc += "  name=%s"             % self.name             + os.linesep
        desc += "  version=0x%x"        % self.version          + os.linesep
        desc += "  type=%i"             % self.type             + os.linesep
        desc += "  start=0x%x"          % self.start            + os.linesep
        desc += "  size=0x%x"           % self.size             + os.linesep
        desc += "  page_size=0x%x"      % self.page_size        + os.linesep
        desc += "  value_empty=0x%x"    % self.value_empty      + os.linesep
        desc += "  prog_timeout_ms=%i"  % self.prog_timeout_ms  + os.linesep
        desc += "  erase_timeout_ms=%i" % self.erase_timeout_ms + os.linesep
        desc += "  sectors:" + os.linesep
        for sector_addr, sector_size in self.sector_info_list:
            desc += ("    addr=0x%x, size=0x%x" %(sector_addr, sector_size) + os.linesep)

        return desc


SymbolSimple = namedtuple("SymbolSimple", "name, value, size")

class ElfFileSimple(ELFFile):
    ''' ELF对象包装,以更方便访问 symbols 和 rom '''

    def __init__(self, data):
        super(ElfFileSimple, self).__init__(io.BytesIO(data))

        self.symbols = {}   # 将 symbol table 中信息读出存入此中,方便访问
        for symbol in self.get_section_by_name(".symtab").iter_symbols():
            self.symbols[symbol.name] = SymbolSimple(symbol.name, symbol["st_value"], symbol["st_size"])

    def read(self, addr, size):     # 从ELF文件中读取程序数据        
        for segment in self.iter_segments():
            seg_addr = segment["p_paddr"]
            seg_size = min(segment["p_memsz"], segment["p_filesz"])
            
            if addr >= seg_addr and addr + size <= seg_addr + seg_size:
                start = addr - seg_addr
                return segment.data()[start : start+size]
            else:
                continue


if __name__ == '__main__':
    # 中断halt程序,让函数执行完后返回到这里来执行从而让CPU自动halt住
    BLOB_HEADER = '0xE00ABE00, 0x062D780D, 0x24084068, 0xD3000040, 0x1E644058, 0x1C49D1FA, 0x2A001E52, 0x4770D1F2,'
    HEADER_SIZE = 0x20

    data_dict = {
        'prog_header': BLOB_HEADER,
        'header_size': HEADER_SIZE,
        'entry': 0x20000000,
        'stack_pointer': 0x20000000 + 4096,
    }

    for name in os.listdir(os.getcwd()):
        if os.path.isfile(name) and name.endswith('.FLM'):
            with open(name, 'rb') as f:
                algo = PackFlashAlgo(f.read())
                print(algo.flash_info)
                if 'algo' in data_dict: del data_dict['algo']
                algo.process_template('c_blob.tmpl', name.replace('.FLM', '.c'), data_dict)
 

c_blob.tmpl

/* Flash OS Routines (Automagically Generated)
 * Copyright (c) 2009-2015 ARM Limited
 */
#include "flash_blob.h"

static const uint32_t flash_code[] = {
    {{prog_header}}
    {{algo.format_algo_data(4, 8, "c")}}
};

const program_target_t flash_algo = {
    {{'0x%08X' % (algo.symbols['Init'] + header_size + entry)}},  // Init
    {{'0x%08X' % (algo.symbols['UnInit'] + header_size + entry)}},  // UnInit
    {{'0x%08X' % (algo.symbols['EraseChip'] + header_size + entry)}},  // EraseChip
    {{'0x%08X' % (algo.symbols['EraseSector'] + header_size + entry)}},  // EraseSector
    {{'0x%08X' % (algo.symbols['ProgramPage'] + header_size + entry)}},  // ProgramPage

    // BKPT : start of blob + 1
    // RSB  : address to access global/static data
    // RSP  : stack pointer
    {
        {{'0x%08X' % (entry + 1)}},
        {{'0x%08X' % (entry + algo.rw_start + header_size)}},
        {{'0x%08X' % (entry + algo.rw_start + header_size + algo.rw_size + algo.flash_page_size + 1024)}}
    },

    {{'0x%08X' % (entry + algo.rw_start + header_size + algo.rw_size)}},  // mem buffer location
    {{'0x%08X' % entry}},  // location to write prog_blob in target RAM
    sizeof(flash_code),  // prog_blob size
    flash_code,  // address of prog_blob
    {{'0x%08X' % algo.flash_page_size}},  // ram_to_flash_bytes_to_be_written
};

工程示例代码:

GitHub - XIVN1987/DAPProg: Offline SWD Programmer for Cortex-M Core MCU


http://www.ppmy.cn/news/95988.html

相关文章

记录一个因变量遮蔽引起的“友尽”级bug

之前在翻译学习EOPL过程中回顾以前的代码时发现一个让人后背发凉的隐患&#xff0c;一种极其罕见、但是一旦出现就难以发现并可能造成非常大影响的bug&#xff0c;本文就记录下这个问题。 问题场景 下面来看一段常见的示例程序&#xff1a; public class DemoActivity exten…

Notion——构建个人知识库

前言 使用Notion快三年了&#xff0c;它All in one的理念在使用以后确实深有体会&#xff0c;一直想找一个契机将这个软件分享给大家&#xff0c;这款笔记软件在网上已经有很多的教程了&#xff0c;所以在这里我主要想分享框架方面的内容给大家&#xff0c;特别对于学生党、研究…

马斯克要用人工智能对抗人工智能

导读&#xff1a;马斯克对人工智能可能变得失控并“摧毁人类”的担忧促使他采取行动&#xff0c;发起了一个名为“TruthGPT”的项目。 本文字数&#xff1a;1400&#xff0c;阅读时长大约&#xff1a;9分钟 亿万富翁埃隆马斯克在谈到人工智能&#xff08;AI&#xff09;的危险时…

QUIC 协议:特性、应用场景及其对物联网/车联网的影响

什么是 QUIC 协议 QUIC&#xff08;Quick UDP Internet Connections&#xff09;是由谷歌公司开发的一种基于用户数据报协议&#xff08;UDP&#xff09;的传输层协议&#xff0c;旨在提高网络连接的速度和可靠性&#xff0c;以取代当前互联网基础设施中广泛使用的传输控制协议…

AI歌手真的可以吗

此为内容创作模板&#xff0c;在发布之前请将不必要的内容删除 你听过AI歌手吗&#xff1f;近日&#xff0c;“AI孙燕姿”火遍全网&#xff0c;AI孙燕姿翻唱林俊杰的《她说》、周董的《爱在西元前》、赵雷的《成都》等等歌曲让网友听了直呼&#xff1a;“听了一晚上&#xff0…

Presto之BroadCast Join的实现

一. 前言 在Presto中&#xff0c;Join的类型主要分成Partitioned Join和Broadcast Join&#xff0c;在Presto 之Hash Join的Partition_王飞活的博客-CSDN博客 中已经介绍了Presto的Partitioned Join的实现过程&#xff0c;本文主要介绍Broadcast Join的实现。 二. Presto中Broa…

RabbitMQ系列(24)--RabbitMQ集群搭建

前言&#xff1a;当RabbitMQ服务器遇到内存崩溃、机器掉电或者主板故障等情况&#xff0c;该怎么办?单台RabbitMQ服务器可以满足每秒1000条消息的吞吐量&#xff0c;那如果应用需要RabbitMQ服务满足每秒10万条消息的吞吐量呢?购买昂贵的服务器来增强单机RabbitMQ服务的性能不…

软考初级程序员上午单选题(20)

36、Windows系统的任务栏不可能出现在屏幕的______。 A&#xff0e;左边 B&#xff0e;右边 C&#xff0e;上边 D&#xff0e;中间 37、下列关于“快捷方式”的叙述中&#xff0c;不正确的是______。 A&#xff0e;可以使用快捷方式作为打开程序的捷径 B&#xff0e;快捷方式的…