离线编程器:
1、首先,既然是SWD编程器,那首先当然是要实现SWD时序协议了
由于单片机都没有SWD外设,所以只能用GPIO模拟实现SWD时序,,这部分功能已经由ARM公司的CMSIS-DAP代码实现
2、然后就是基于CMSIS-DAP,实现通过DAP读写目标芯片的内存、内核寄存器,,这部分功能已经由DAPLink里面的swd_host.c文件实现
同时,swd_host.c还实现了另一个对实现编程器至关重要的函数:
- 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