使用sdk-npi-enablement-tool生成SVD文件和芯片头文件
文章目录
Introduction
芯片验证与测试组(VV,Verification & Validation Team)的同事在验证新芯片产品时,需要在手册完备之前就要开始展开开发工作,甚至需要根据验证的结果对从设计文档合成的初版用户手册进行调整。这些早期的开发工作也会对软件工具的设备支持包提出需求,例如Keil集成开发环境的pack支持包,但正式给用户发布的设备支持包又需要依赖于完备的用户手册。好吧,这其实是一个“鸡蛋问题”(到底是先有鸡再生蛋,还是先有蛋再孵小鸡)。暂且跳过最初的情况,我们软件与系统工程组(Software & System Engineering Team)的工程师可以基于已有的相近产品进行微调,然后同VV提供的验证情况配合,不断微调和迭代趋近最终发布状态。这才是真实世界的开发过程。在这个过程中,对描述寄存器布局的SVD文件的更新是最活跃的,可能随着验证的实际情况,我们希望不要每次都重头开始处理所有外设寄存器的布局,而是不断根据每个周期的反馈,进行局部更新,以减少重复的工作量。在这个工作环境中,在现有SVD文件局部更新某个外设模块,将会是一个非常频繁基本操作。
本文详细说明了如何使用灵动软件与系统工程组自主开发的sdk-npi-enablement-tool
工具包,完成SVD文件局部更新的操作过程。
Overview
sdk-npi-enablement-tools
工具包含了一个描述外设模块寄存器分布信息的数据库(xlsx文件和yaml文件集合),以及以数据库为对象的一系列服务的工具,例如,从原始的doc格式的UM文档中提取寄存器分布信息存放为xlsx记录的工具类 gen_srctables.py
、从描述外设模块寄存器分布的xlsx文件生成SVD文件的工具类 gen_svdfile.py
、从xlsx记录中生成包含中断向量表等具体芯片相关信息头文件和dma_request.h
文件的gen_sdkfile.py
等。
Operation Steps
以新建的不会在真实世界中存在的虚拟平台MM32F0000
项目为例,以现有已经发布的MM32F0020
微控制器的SVD文件为模板,更换其中的UART
模块为USART
模块,产生最终的MM32F0000
微控制器项目的SVD文件。
创建芯片配置文件yaml
- 在
sdk-npi-enablement-tools\tools\generator\yaml\devices
目录下创建MM32F0000.yaml
文件。这里的MM32F0000.yaml
文件描述了SoC中包含了哪些外设模块。
此处以MM32F0020
项目在同级目录下的配置文件为模板,复制创建MM32F0000.yaml
的芯片配置文件。其中包含了MM32F0000
芯片中集成的外设模块。这些记录的信息来自于芯片DS手册中对片上资源介绍的描述,也可以来自于产品经理定义新芯片产品的PB文档,但实际芯片集成资源的情况仍需以DS为准。
coretype: m0
subseries:
MM32F0000B:
peripherals:
peripherals:
flash:
pwr:
rcc:
crc:
syscfg:
gpio:
gpioa:
gpiob:
exti:
adc:
adc1:
tim1:
tim3:
tim14:
wwdg:
iwdg:
uart:
uart1:
uart2:
spi:
spi1:
i2c:
i2c1:
dbg:
其中,yaml文件名描述了本产品系列的名字,yaml文件中的subseries
及下级属性描述了产品系列内部子系列具体芯片型号的特有外设,例如在MM32F0270.yaml
文件中,在不同的子系列中,对usb、can等模块的支持情况就进行了细分。
- 在
sdk-npi-enablement-tools\database\ip_reg_desp
目录下为MM32F0000
创建外设寄存器映射数据记录。
此处以同级目录下的MM32F0020
目录为模板,复制成MM32F0000
的目录。其中包含了以xlsx文件存放的各外设模块独自的寄存器映射地址信息。这些文件记录的原始数据来自于芯片UM手册中对外设模块的描述,也可以来自于芯片设计组(芯片架构)提供的完备的寄存器描述材料。
-
在
sdk-npi-enablement-tools\resource
目录下为MM32F0000
芯片创建关于中断向量表和外设基址映射信息的记录。此处仍以同级目录下的
MM32F0020
项目为模板,复制成MM32F0000
项目的目录。其中包含了baseaddress.xlsx
文件和interrupts.xlsx
文件,分别记录外设模块的基地址和中断向量表中各中断向量的名字和排序。这些记录的原始信息来自于芯片的UM手册。
- 运行
sdk-npi-enablement-tools\tools
目录下的gen_svdfile.py
脚本程序,将在``sdk-npi-enablement-tools\targetfiles目录创建一个
mm32f0000`的项目目录,其中包含本次创建过程生成的SVD文件。
在脚本中修改chiptype的配置值为芯片名,mm32f0000,此处的设定值需要同MM32F0000.yaml
文件的文件名保持一致。
import os
from generator.gen_regsvd import *
def main():
chiptype = 'mm32f0000' # change here to suit to different chip series
wkdir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))
outfiledir = os.path.join(wkdir, 'targetfiles', chiptype) # Easy to manage, the generated file path is the specified path under the specific series of "targetfiles"
# write register definition
regdef = GenReg(chiptype)
regdef.write_svd()
if __name__ == '__main__':
main()
运行。成功。可以看到,在targetfiles
新建的mm32f0000目录下,生成了MM32F0000.svd
文件。
运行成功。
此时,我们已经通过sdk-npi-enablement-tool
得到了一个MM32F0000
芯片项目的SVD文件。这个新生成的文件实际的内容还是现有MM32F0020
项目的,仍需要调整其中的内容。但无论如何,创建新芯片项目SVD文件的框架已经搭起来了。接下来,需要解决向其中添加新外设模块USART
和删减外设模块UART
的问题。
填充外设模块的寄存器映射描述文件xlsx
在上一个环节中,我们已经通过sdk-npi-enablement-tool
得到了一个MM32F0000
芯片项目的SVD文件,创建新芯片项目SVD文件的框架和操作流已经搭起来了。现在,需要解决删减外设模块UART并向其中添加新外设模块USART的问题。
在现有项目中删除模块相对容易,直接在上文提到的MM32F0000.yaml文件中删除不需要的模块的记录,然后在baseaddr.xlsx
和interrupts.xlsx
文件中删除对应的记录即可。如果要做得更彻底,还可以继续删掉sdk-npi-enablement-tool\database\ip_reg_desp\mm32f0000
目录下描述UART模块寄存器映射的uart.xlsx文件,但因为在上述配置文件中已经删除了对UART模块的引用,所以此处的uart.xlsx文件被删与否都不会再起作用。
在现有项目中添加新外设同删除模块对等,只要在需要引用模块的地方添加新模块的记录即可。例如,这里将UART模块的名字改为USART(相当于删掉了UART模块后再添加USART)。
- 在
sdk-npi-enablement-tool\tools\generator\yaml\devices
目录下的mm32f0000.yaml
文件中,添加USART
模块的记录 - 在
sdk-npi-enablement-tool\resource\mm32f0000
目录下的baseaddress.xlsx
文件中,添加USART模块在存储空间中的基地址 - 在
sdk-npi-enablement-tool\resource\mm32f0000
目录下的interrupts.xlsx
文件中,添加USART模块在中断向量表中的记录 - 在
sdk-npi-enablement-tool\database\ip_reg_desp\mm32f0000
目录下,为USART模块创建描述寄存器及其位域的表格,这个需要如下的操作,从UM手册文档中提取内容,才能创建生成。
目前,我有USART
模块的IP Spec文档IP_USART_DesignSpec_v0.x.docx
,其中包含了对USART模块中各寄存器及其位域的地址映射信息。
配置sdk-npi-enablement-tool\tools
目录下的gen_srctables.py
脚本:
- 指定输入文件为包含USART模块中各寄存器及其位域的地址映射信息的
IP_USART_DesignSpec_v0.x.docx
,指定位于sdk-npi-enablement-tool\resource\mm32f0000
目录下。 - 指定输出路径在
sdk-npi-enablement-tool\database\ip_reg_desp\mm32f0000
下,脚本会根据mm32f0000.yaml
文件中指定的模块清单,匹配传入文件中的内容,如果在mm32f0000.yaml
文件中登记并在传入文件中找到相关模块的记录,则会在指定路径下创建这个模块的单独的xlsx文件。
#!usr/bin/python3
#! encoding utf-8
from generator.gen_tables import GenTable
def main():
RegTable = GenTable('mm32f0000', 'IP_USART_DesignSpec_v0.x.docx') # 需修改两个参数,第一个芯片系列(需小写),第二个为文件Word文件名
print('Data extraction finished.')
if __name__ == '__main__':
main()
运行程序后,在sdk-npi-enablement-tool\database\ip_reg_desp\mm32f0000
目录下生成了新的usart.xlsx文件。
此时,因为已经更新了yaml文件中的配置信息,重新执行上节中生成SVD文件的操作步骤,即可产生包含USART模块的全新的SVD文件。
其中,关于寄存器字段的描述是中文的,Keil的SVD解析器仅能识别英文。此处需要进行一些微调。故建议使用英文版手册作为SVD生成过程的数据源,或者一种可行的做法,是直接使用寄存器位域名取代位域的解释,至少先保证SVD文件能够正常被SVD文件解析器识别。本例中直接修改USART模块的数据源(寄存器映射表)文件,如图x所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lT9fhDp8-1674062199216)(./image/figure-usart-ip-srctable-modifed.png)]
重新运行gen_svdfile.py
,更新成功。
验证
使用sdk-npi-enablement-tool
工具包生成的SVD文件,不一定能够被Keil的SVD文件完全识别。为此,在正式交付之前,需要验证SVD文件是否可用。CMSIS-SVD提供了一个工具 svdconv.exe
,可以将SVD文件转换成各种其他源文件,其中最基本的功能,就是验证SVD文件的有效性。
在sdk-npi-enablement-tool\tools
目录下找到SVDConv.exe
工具,并在命令行下运行,传入刚生成的mm32f0000.svd文件。
indMotion@PF2LD92H MINGW64 /d/workspace/sdk-npi-enablement-tool/tools
$ svdconv.exe
CMSIS-SVD SVD Consistency Checker / Header File Generator V3.3.42
Copyright (C) 2010-2022 ARM Ltd and ARM Germany GmbH. All rights reserved.
Usage:
SVDConv.exe <SVD file> [Options]
Options:
-o <output path> Output Path, default current directory
-b <log file> Log file for Messages
--generate=header Generate CMSIS header file
--fields=struct Generate struct/union for bitfields
--fields=macro Generate macros for bitfields
--fields=struct-ansic Generate structs for bitfields (ANSI C)
--fields=enum Generate enumerated values for bitfields
--debug-headerfile Add Addresses to Registers comments
--generate=partition Generate CMSIS partition file
-x [Mxxx, INFO, WARNING] Suppress Message, Warnings or Infos
-h, -? Show this help
Examples:
Check only> SVDConv.exe myDevice.svd
Header Generation> SVDConv.exe myDevice.svd --generate=header
MindMotion@PF2LD92H MINGW64 /d/workspace/sdk-npi-enablement-tool/tools
$ svdconv.exe MM32F0000.svd
CMSIS-SVD SVD Consistency Checker / Header File Generator V3.3.42
Copyright (C) 2010-2022 ARM Ltd and ARM Germany GmbH. All rights reserved.
Arguments: "MM32F0000.svd"
...
** INFO M304: MM32F0000.svd (Line 6029)
Interrupt number overwrite: '19 : TIM14' (Line 5715)
Found 26 Error(s) and 0 Warning(s).
这里发现有26个error。经排查发现,是因为存在有的寄存器地址用不同的名字命名。
*** ERROR M339: MM32F0000.svd (Line 5128)
Register 'CCMR1_INPUT' (read-write) (@0x00000018:32) has same address or overlaps 'CCMR1_OUTPUT' (read-write) (@0x00000018:32) (Line 5069)
*** ERROR M339: MM32F0000.svd (Line 5128)
Register 'CCMR1_INPUT' (read-write) (@0x00000018:32) has same address or overlaps 'CCMR1_OUTPUT' (read-write) (@0x00000018:32) (Line 5069)
这类问题需要在SVD文件中对应寄存器的定义中添加<alternateRegister>
标签。例如在CCMR_Input中的写法。注意,在后续版本的sdk-npi-enablement-tool
中,也将会加入这样的生成策略,但在目前版本中,需要手动添加,以清理错误。
<register>
<name>CCMR1_Input</name>
<displayName>CCMR1_Input</displayName>
<description>capture/compare mode register 1 (input mode)</description>
<alternateRegister>CCMR1_Output</alternateRegister>
<addressOffset>0x18</addressOffset>
<size>0x20</size>
<access>read-write</access>
<resetValue>0x00000000</resetValue>
<fields>
<field>
<name>IC2F</name>
<description>Input capture 2 filter</description>
<bitOffset>12</bitOffset>
<bitWidth>4</bitWidth>
</field>
另外,还有一些其他细微的地方需要人工处理,例如,由于sdk-npi-enablement-tool
中脚本使用的正则表达式没有完整呈现出寄存器命名的套路,有一些寄存器命名存在重叠、重复等等,这些问题在当前版本的软件中,处理能力有限。后续版本会针对这些情况做一些限定,或者使用更强规则的正则表达式对寄存器名进行匹配和甄别。
总之,最后清理掉所有的ERROR就算大功告成。
生成芯片头文件
sdk-npi-enablement-tool
工具包中的gen_sdkfile.py
工具,可用于生成芯片头文件。实际上CMSIS-SVD中的SVDConv.exe工具也可以从SVD文件生成CMSIS规范的头文件,但我们需要生成头文件的宏定义要全部大写,而不是SVDConv.exe工具生成的存在部分小写字母的宏定义代码,因此,仍然需要gen_sdkfile.py
工具从sdk-npi-enablement-tool
工具包的数据库中生成描述全部芯片外设模块在存储空间中映射关系的芯片头文件。在目前正在开发的版本中,生成头文件的机制将有所改变,届时将会基于SVD文件生成头文件,类似于实现了一个Python版本的SVDConv.exe。
在sdk-npi-enablement-tool\tools
目录下配置脚本文件gen_sdkfile.py
,指定chiptype
的值为mm32f0000,然后,运行。
#!usr/bin/python3
#! encoding utf-8
import os
import generator.soc_info
from generator.gen_dma_requests import GenDMARequests
from generator.gen_interrupts import GenInt
from generator.gen_regsvd import GenReg
from generator.gen_baseaddr import GenBase
def main():
chiptype = 'mm32f0000' # change here to suit to different chip series
# output dir.
wkdir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))
outfiledir = os.path.join(wkdir, 'targetfiles', chiptype)
# generate interruupt vector for startup file.
interrupts = GenInt(chiptype)
interrupts.get_sVector(outfiledir)
# generate header file for registers.
regdef = GenReg(chiptype)
regdef.write_reg()
if __name__ == '__main__':
main()
运行成功后,在sdk-npi-enablement-tool\targetfiles\mm32f0000
目录下生成了新文件MM32F0000\mm32f0000.h
和interrupt.h
文件,其中,interrupt.h
文件中包含中断服务函数名的清单,这个清单可用于进一步创建在不同工具链下的启动代码。生成的mm32f0000.h
文件,即为可发布的芯片头文件。
至此,我们已经为新芯片项目MM32F0000
生成了芯片头文件。
Conclusion
在本文中,记录了使用sdk-npi-enablement-tool
生成设备支持包中描述芯片寄存器存储映射的SVD文件和芯片头文件的过程。
- 在执行操作之前,需要准备好包含外设模块寄存器描述信息的UM文档的docx版本,
sdk-npi-enablement-tool
中的脚本会解析docx文件,从中提取描述寄存器及其位域的表格,先进行数据清洗,然后以xlsx文件的格式,将各个外设模块的寄存器寄存器映射信息分别保存成独立的文件。这些文件同IC设计的IP仓库一一对映,形成外设模块寄存器映射的数据库。 - 需要在yaml数据库中创建新芯片的记录文件,这些文件描述了某一款芯片包含哪些外设模块,相当于是IC设计过程中的SoC集成过程。
- 然后,就可以调用一系列脚本工作逐步生成需要的文件
- 使用
gen_srctables.py
从原始的doc格式的UM文档中提取寄存器分布信息存放为xlsx记录 - 使用
gen_svdfile.py
从描述外设模块寄存器分布的xlsx文件生成SVD文件 - 使用
gen_sdkfile.py
从xlsx记录中生成包含中断向量表等具体芯片寄存器清单的头文件
- 使用
sdk-npi-enablement-tool
工具包目前保持在迭代更新当中,仍存在一些问题,需要少量的人工介入才能通畅地完成生成文件的过程,但已经能够作为生产力工具对微控制器产品的软件开发工作提供完备的技术服务。在接下来将要发布的版本中,将会不断修复已知问题,优化使用体验。