代码仓库地址:openocd-extension: Fork of openocd-org/openocd(https://github.com/openocd-org/openocd)Openocd with third party target suppporthttps://gitee.com/char-x/openocd-exptension

参加合泰杯比赛的时候拿到了一片HT32F52352开发板,寻思着初赛准备时间比较长,于是就借此机会学习一下在Linux中使用OpenOCD调试ARM设备。

开发板上板载了一个e-link32 Lite,其实就是一个CMSIS-DAP,不过只支持SWD方式。在Holtek官网找到芯片手册,芯片大致信息:

  • 32-bit Arm® Cortex®-M0+ 处理器内核(基于ARMv6-M)
  • 128KB 片上 Flash 存储器用作指令 / 数据和选项存储
  • 16KB 片上 SRAM
  • Flash 存储器控制器 – FMC
  • 内部总线矩阵连接 AHB-Lite 接口,单循环 I/O 口和调试访问端口 (DAP)
  • 串行线调试端口 (SW-DP)

ARM芯片大多数都遵循CMSIS协议,OpenOCD也自带了调试接口,这也就意味着芯片的DAP不需要我们自己设计。而 HT32F52352芯片没有被OpenOCD官方分支收录(怎么会被收录呢),这个其实就是没有对应的target规则和flash烧写规则,需要我们自己去编写,因此本文主要是编写针对HT32F52352的target文件和flash文件。

要让OpenOCD能够找到我们编写的芯片信息,需要进行如下步骤:(引用)

在./tcl/target中,添加自己设备的.cfg文件,此文件用于编译后传递到scripts中供用户调用,此文件用于编译后传递到scripts中供用户调用

在./src/flash/nor中,添加自己设备的.c文件,此文件用于告知openocd你文件的flash烧录流程,此文件用于告知openocd你文件的flash烧录流程

在 /src/flash/drivers.c中,添加自己设备的设备名,此处让openocd知晓你的设备.,此处让openocd知晓你的设备.

在flash/nor中,更改Makefile.am,令openocd的编译过程可以引用到你刚刚添加的文件.,令openocd的编译过程可以引用到你刚刚添加的文件.

ht32f523xx.cfg

target文件参照自带的stm32l0.cfg来改写

根据OpenOCD官方的文档,一个target的cfg文件应该以如下方式开头:

source [find target/swj-dp.tcl]
source [find mem_helper.tcl]

if { [info exists CHIPNAME] } {
   set _CHIPNAME $CHIPNAME
} else {
#stm32l0 refer to your device
   set _CHIPNAME stm32l0
}

贴出完整文件:(ht32f523xx.cfg),这个比较简单,看注释即可

#
# M0+ devices only have SW-DP, but swj-dp code works, just don't
# set any jtag related features
#

source [find target/swj-dp.tcl]
source [find mem_helper.tcl]

if { [info exists CHIPNAME] } {
   set _CHIPNAME $CHIPNAME
} else {
   set _CHIPNAME ht32f5xxxx
}

set _ENDIAN little

# Work-area is a space in RAM used for flash programming
# By default use 2kB (max ram on smallest part)
if { [info exists WORKAREASIZE] } {
   set _WORKAREASIZE $WORKAREASIZE
} else {
   set _WORKAREASIZE 0x0800
}

# JTAG speed should be <= F_CPU/6.
# F_CPU after reset is ~2MHz, so use F_JTAG max = 333kHz
adapter speed 300

adapter srst delay 100

if { [info exists CPUTAPID] } {
    set _CPUTAPID $CPUTAPID
} else {
    # Arm, m0+, non-multidrop.
    # http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka16088.html
    # you can also find this id in keil MDK
    set _CPUTAPID 0x0bc11477
}

swj_newdap $_CHIPNAME cpu -expected-id $_CPUTAPID
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu

#diffirent of v0.10.x,in OpenOCD v0.11.x it should use -dap instead of -chain-position
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap

$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0

# flash size will be probed
set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME stm32lx 0 0x00020000 0 0 $_TARGETNAME

reset_config srst_nogate

#I don't know what the following is
if {![using_hla]} {
   # if srst is not fitted use SYSRESETREQ to
   # perform a soft reset
   cortex_m reset_config sysresetreq
}
$_TARGETNAME configure -event reset-start {
  
  #default speed
  adapter speed 300
}

$_TARGETNAME configure -event examine-end {
  
  #close DBG_WDT
  mmw 0x40088304 0x00000008 0
}


ht32f523xx.c

此处开始讨论flash处理程序

首先要知道芯片片内FLASH的操作外设信息,此处贴出HT32F52352的官网资料:(主要信息)

片内FLASH控制器为FLASH Manage Controller(FMC),其挂载在AHB总线上,复位后默认时钟为开启。FMC提供页擦除,整片擦除,字编程,页保护等功能

操作步骤具体参考芯片厂家的描述,对于HT32F52352的FMC,其字编程流程为:

  1. 写入编程地址(4 bytes align)到TADR寄存器(0x000 offset)
  2. 写入要写入的数据(4 bytes)到WRDR寄存器 (0x004 offset)
  3. 向OCMR寄存器的CMD[3:0]位写入操作命令{0x4 字编程;0x8 页擦除;0xA 整片擦除} (0x00C offset)
  4. 检查OPCR寄存器的OPM[4:1]位 {0x6 第一次前空闲,0xE 操作完成空闲,other FMC繁忙} (0x010 offset)
  5. 当OPM为0x6活0xE时,向OPM写入0xA以执行命令
  6. 完成

 在flash中主要要做的是把FMC各个外设寄存器的地址以及操作命令,状态参数等通过宏定义声明在文件内。对每个实现构造函数,openocd的编辑脚本采用的是严格的编译检查,因此每个需要被回调的函数的返回值,参数类型,参数个数都要于openocd声明的严格一致。

参照官方的stm32lx.c(l0系列也是Cortex-M0+)以下是写出的程序:

程序中其实还有一些部分没有实现,主要是片内flash保护和info获取,因为本人觉得这两个功能不怎么用得上,需要的话按照手册上的说明构造对应的函数,在flash_driver结构体的protect元素中赋值对应函数的指针即可。

(可以clone源码再来看,文件路径是 src/flash/nor/ht32f523xx.c)

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "imp.h"
#include <helper/binarybuffer.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
#include <target/cortex_m.h>

/*******HT32F523xx Flash Manager Contorler(FMC) regisister location*******/
#define FMC_REG_BASE  0x40080000  /*FLASH Manager Contorller base addr   */
/*************************************************************************/
#define FMC_REG_TADR  0x000  /*FLASH ADDR REG                            */
#define FMC_REG_WRDR  0x004  /*Data for writing REG                      */
#define FMC_REG_OCMR  0x00C  /*Operation CMD REG                         */
#define FMC_REG_OPCR  0x010  /*Operation CTRL REG                        */
#define FMC_REG_OIER  0x014  /*Interrupt ENABLE REG                      */
#define FMC_REG_OISR  0x018  /*Interrupt STATUS REG                      */
#define FMC_REG_PPSR  0x020  /*Page erase/Programming protect STATUS REG */
#define FMC_REG_CPSR  0x030  /*Safety protection STATUS REG              */
#define FMC_REG_VMCR  0x100  /*Vector mapping CTRL REG                   */
#define FMC_REG_MDID  0x180  /*Manufacture and Microcontroller ID REG    */
#define FMC_REG_PNSR  0x184  /*Pages STATUS REG                          */
#define FMC_REG_PSSR  0x188  /*Size of page STATUS REG                   */
#define FMC_REG_CFCR  0x200  /*Cache and pre-read control REG            */
#define FMC_REG_CIDR0 0x310  /*User-defined ID REG0                      */
#define FMC_REG_CIDR1 0x314  /*User-defined ID REG1                      */
#define FMC_REG_CIDR2 0x318  /*User-defined ID REG2                      */
#define FMC_REG_CIDR3 0x31C  /*User-defined ID REG3                      */
/*************************************************************************/

/***************************FMC OPRC regisister***************************/
#define FMC_OPM_MASK  0x1E
#define FMC_COMMIT   (0xA << 1)
#define FMC_FINISHED (0xE << 1)
#define FMC_START    (0x6 << 1)
/*************************************************************************/

/***************************FMC OCMR regisister***************************/
#define FMC_CMD_MASK        0xF
#define FMC_CMD_WORD_PROG   0x4
#define FMC_CMD_PAGE_ERASE  0x8
#define FMC_CMD_MASS_ERASE  0xA
/*************************************************************************/

/***************************FMC option byte regisister***************************/
#define OPTION_BYTE_BASE 0x1FF00000
#define OPT_OB_PP 0x000
#define OPT_OB_CP 0x010

/*************************************************************************/

#define FLASH_ERASE_TIMEOUT  1000  /*timeout count*/

/*Command flash bank ht32f523xx <base> <size> 0 0 <target>*/
FLASH_BANK_COMMAND_HANDLER(ht32f523xx_flash_bank_command)
{
    if(CMD_ARGC < 6)
        return ERROR_COMMAND_SYNTAX_ERROR;
    
    bank->driver_priv = NULL;

    return ERROR_OK;
}

static inline int ht32f523xx_get_flash_status(struct flash_bank *bank, uint32_t *status)
{
    struct target *target = bank->target;
    return target_read_u32(target,FMC_REG_BASE + FMC_REG_OPCR, status);
}

static int ht32f523xx_wait_status_busy(struct flash_bank *bank,int timeout)
{
    uint32_t status;
    int retval = ERROR_OK;

    for(;;){
        retval = ht32f523xx_get_flash_status(bank,&status);
        if(retval != ERROR_OK){
            return retval;
        }
        if( ((status & FMC_OPM_MASK)==FMC_FINISHED) || ((status & FMC_OPM_MASK) == FMC_START) ){
            return ERROR_OK;
        }
        if(timeout-- <= 0){
            LOG_DEBUG("Timed out waiting for flash: 0x%04x", status);
            return ERROR_FAIL;
        }
        alive_sleep(10);
    }

    return retval;
}

static int ht32f523xx_erase(struct flash_bank *bank, unsigned int first, unsigned int last)
{
    struct target *target = bank->target;

    LOG_DEBUG("ht32f523xx erase: %d - %d", first, last);

    if(target->state != TARGET_HALTED){
        LOG_ERROR("Target not halted");
        return ERROR_TARGET_NOT_HALTED;
    }

    for(unsigned int i = first ; i <= last; ++i){
        /*flash memory page erase*/
        int retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_TADR,bank->sectors[i].offset);
        if(retval != ERROR_OK){
            return retval;
        }

        retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_OCMR,FMC_CMD_PAGE_ERASE);
        if(retval != ERROR_OK){
            return retval;
        }

        retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_OPCR,FMC_COMMIT);
        if(retval != ERROR_OK){
            return retval;
        }

        //wait
        retval = ht32f523xx_wait_status_busy(bank,FLASH_ERASE_TIMEOUT);
        if(retval != ERROR_OK){
            return retval;
        }

        LOG_DEBUG("HT32F523xx erased page %d", i);
        bank->sectors[i].is_erased = 1;
    }

    return ERROR_OK;
}

static int ht32f523xx_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last)
{
    return ERROR_FLASH_OPER_UNSUPPORTED;
}

static int ht32f523xx_write(struct flash_bank *bank, const uint8_t *buffer,uint32_t offset, uint32_t count)
{
    struct target *target = bank->target;

    LOG_DEBUG("ht32f523xx flash write: 0x%x 0x%x", offset, count);

    if(target->state != TARGET_HALTED){
        LOG_ERROR("Target not halted");
        return ERROR_TARGET_NOT_HALTED;
    }
    if(offset & 0x03){
        LOG_ERROR("offset 0x%" PRIx32 " breaks required 4-byte alignment", offset);
        return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
    }
    if(count & 0x3){
        LOG_ERROR("size 0x%" PRIx32 " breaks required 4-byte alignment", count);
        return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
    }

    uint32_t addr = offset;
    for(uint32_t i = 0; i < count; i += 4){
        uint32_t word = (buffer[i]   << 0)  |
                        (buffer[i+1] << 8)  |
                        (buffer[i+2] << 16) |
                        (buffer[i+3] << 24);

        LOG_DEBUG("ht32f523xx flash write word 0x%x 0x%x 0x%08x", i, addr, word);

        // flash memory word program
        int retval;
        retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_TADR, addr);
        if (retval != ERROR_OK)
            return retval;
        retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_WRDR, word);
        if (retval != ERROR_OK)
            return retval;
        retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_OCMR, FMC_CMD_WORD_PROG);
        if (retval != ERROR_OK)
            return retval;
        retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_OPCR, FMC_COMMIT);
        if (retval != ERROR_OK)
            return retval;

        // wait
        retval = ht32f523xx_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
        if (retval != ERROR_OK)
            return retval;
        addr += 4;
    }

    LOG_DEBUG("ht32f523xx flash write success");
    return ERROR_OK;
}

static int ht32f523xx_probe(struct flash_bank *bank)
{
    int page_size = 512;
    int num_pages = bank->size / page_size;

    LOG_INFO("ht32f523xxx probe: %d pages, 0x%x bytes, 0x%x total", num_pages, page_size, bank->size);

    if(bank->sectors){
        free(bank->sectors);
    }

    bank->base = 0x0;
    bank->num_sectors = num_pages;
    bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);

    for(int i = 0; i < num_pages; ++i){
        bank->sectors[i].offset = i * page_size;
        bank->sectors[i].size = page_size;
        bank->sectors[i].is_erased = -1;
        bank->sectors[i].is_protected = 1;
    }

    return ERROR_OK;
}

static int ht32f523xx_auto_probe(struct flash_bank * bank)
{
    return ht32f523xx_probe(bank);
}

static int ht32f523xx_protect_check(struct flash_bank *bank)
{
    struct target *target = bank->target;
    uint32_t ob_pp[4];
    uint32_t ob_cp;
    
    //read OB_PP REG
    for(int i=0; i<4; ++i)
    {
        target_read_u32(target,(OPTION_BYTE_BASE + OPT_OB_PP) + (i << 2),ob_pp + i);
    }

    //read OB_CP REG
    target_read_u32(target,(OPTION_BYTE_BASE + OPT_OB_CP),&ob_cp);

    LOG_INFO("ht32f523xx opt byte: %04x %04x %04x %04x %04x", ob_pp[0], ob_pp[1], ob_pp[2], ob_pp[3], ob_cp);
    
    // Set page protection
    for(int i = 0 ; i < 128; ++i){
        int bit = (ob_pp[i / 32] << (i % 32)) & 1;
        bank->sectors[2*i].is_protected = bit ? 0 : 1;
        bank->sectors[(2*i)+1].is_protected = bit ? 0 : 1;
    }

    return ERROR_OK;
}

static int ht32f523xx_info(struct flash_bank *bank, struct command_invocation *cmd)
{
    command_print_sameline(cmd, "ht32f523xx");
    return ERROR_OK;
}

static int ht32f523xx_mass_erase(struct flash_bank *bank)
{
    struct target *target = bank->target;

    if (target->state != TARGET_HALTED) {
        LOG_ERROR("Target not halted");
        return ERROR_TARGET_NOT_HALTED;
    }

    //WITHOUT FMC_BUSY CHECK?!

    // flash memory mass erase
    int retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_OCMR, FMC_CMD_MASS_ERASE);
    if (retval != ERROR_OK)
        return retval;
    retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_OPCR, FMC_COMMIT);
    if (retval != ERROR_OK)
        return retval;

    retval = ht32f523xx_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
    if (retval != ERROR_OK)
        return retval;

    return ERROR_OK;
}

COMMAND_HANDLER(ht32f523xx_handle_mass_erase_command)
{
    if(CMD_ARGC < 6){
        return ERROR_COMMAND_SYNTAX_ERROR;
    }

    struct flash_bank *bank;
    int retval = CALL_COMMAND_HANDLER(flash_command_get_bank,0,&bank);
    if(retval != ERROR_OK){
        return retval;
    }

    retval = ht32f523xx_mass_erase(bank);
    if (retval == ERROR_OK) {
        // set all sectors as erased
        unsigned int i;
        for (i = 0; i < bank->num_sectors; i++)
            bank->sectors[i].is_erased = 1;

        //command_print(CMD_CTX, "ht32f523xx mass erase complete");
    } else {
        //command_print(CMD_CTX, "ht32f523xx mass erase failed");
    }

    return ERROR_OK;
}

COMMAND_HANDLER(ht32f523xx_handle_test_write)
{
    if(CMD_ARGC < 6){
        return ERROR_COMMAND_SYNTAX_ERROR;
    }

    struct flash_bank *bank;
    int retval = CALL_COMMAND_HANDLER(flash_command_get_bank,0,&bank);
    if(retval != ERROR_OK){
        return retval;
    }

    uint8_t buffer[32];
    for(int i = 0; i < 32; ++i){
        buffer[i] = i;
    }

    retval = ht32f523xx_erase(bank, 0, 0);
    if (retval != ERROR_OK)
        return retval;

    retval = ht32f523xx_write(bank, buffer, 0, 32);
    if (retval == ERROR_OK) {
        //command_print(CMD_CTX, "ht32f523xx test write complete");
    } else {
        //command_print(CMD_CTX, "ht32f523xx test write failed");
    }

    return retval;
}

static const struct command_registration ht32f523xx_exec_command_handlers[] = {
    {
        .name = "mass_erase",
        .handler = ht32f523xx_handle_mass_erase_command,
        .mode = COMMAND_EXEC,
        .usage = "bank_id",
        .help = "test flash write",
    },
    {
        .name = "test_write",
        .handler = ht32f523xx_handle_test_write,
        .mode = COMMAND_EXEC,
        .usage = "bank_id",
        .help = "test flash write",
    },
    COMMAND_REGISTRATION_DONE
};

static const struct command_registration ht32f523xx_command_handlers[] = {
    {
        .name = "ht32f523xx",
        .mode = COMMAND_ANY,
        .help = "ht32f523xx flash command group",
        .usage = "",
        .chain = ht32f523xx_exec_command_handlers,
    },
    COMMAND_REGISTRATION_DONE
};

const struct flash_driver ht32f523xx_flash = {
    .name = "ht32f523xx",
    .commands = ht32f523xx_command_handlers,
    .flash_bank_command = ht32f523xx_flash_bank_command,

    .erase = ht32f523xx_erase,
    .protect = ht32f523xx_protect,
    .write = ht32f523xx_write,
    .read = default_flash_read,
    .probe = ht32f523xx_probe,
    .auto_probe = ht32f523xx_auto_probe,
    .erase_check = default_flash_blank_check,
    .protect_check = ht32f523xx_protect_check,
    .info = ht32f523xx_info,
};

其中COMMAND_HANDLER        FLASH_BANK_COMMAND_HANDLER        COMMAND_HELPER 这三个宏都是定义函数,其接受多参数输入,上面所有的函数定义最终都是实现最后面三个结构体需要的回调函数。


在编写好flash的.c文件之后,修改nor目录下的drivers.c,把ht32f523xx_flash声明为全局并把其指针加入下面的指针数组。完成后修改nor目录下的Makefile.am,加入对ht32f523xx.c的编译。回到主目录:

user@localhost:./bootstarp && ./configure && make && sudo make install

这样就安装完成了。下面放一些运行的效果图:

图:openocd连接设备

图:telnet连接openocd服务器并进行烧录

(可以看到烧录很慢,即便把adapter speed提高到1MHz也要3.84s,针对这个问题正在设法解决)

在此补充一下openocd在telnet下面的常规烧录步骤:

#halt the cpu
halt
#write hex
flash write-image erase xxx.hex
#reset
reset

小结:

        这个是我第一次为OpenOCD添加设备支持,也是我第一次动手改开源代码(之前改过一次6relayd的只是修改CMakeLists.txt去使用交叉编译和静态链接)。前前后后我其实已经用了两个多星期。因为不管在国内还是国外网站关于OpenOCD的使用大多是基于官方自带的芯片库(以STM32为主),对于添加第三方芯片的教程几乎没有,刚开始的时候是一直看芯片手册,然后是看OpenOCD官方的User's Guide 和 Developer's Guide。不过说实话,OpenOCD官方的文档中似乎并没有设计第三方芯片添加需要进行的关键操作,其只是在cfg文件格式和命令上有比较大的篇幅。

主要参考资料:(其实参考了很多很多,不过大部分都忘记记录了)

OpenOCD 官方手册

HT32F52342_52 UserManuals

OpenOCD RISCV

 

Ubuntu 下使用OpenOCD进行烧录

https://github.com/ChaoticEnigma/openocd-ht32

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐