前文对ARM SoC的debug和Trace做了介绍,并详细介绍了如何对新的SoC平台进行配置,本文将对平台配置的文件进行解析,充分了解DTSL的工作原理,并对常出现的问题如何进行DTSL脚本修改做阐述。
前文提到一个平台配置需要的三个主要文件分别是SoC的配置文件xxx.rvc,调试描述文件project_types.xml和DTSL脚本文件dtsl_config_scripts.py。这三个文件中,调试描述文件project_types.xml是直接和DS5进行交互的,包括UI界面和底层参数传递的入口,而SoC的配置文件xxx.rvc和DTSL脚本文件是在此文件中被引用的,同时也引用了dstream或者rvi的描述和连接等文件。
SoC的描述rvc文件也是基于xml的,其内容是文本的描述文件,分别描述了SoC各个Debug和Trace模块的属性。在debug和trace中的功能实现,都是基于dtsl_config_scripts.py的DTSL的脚本,这个脚本是基于jython的,比较易懂和便于修改。
DTSL的脚本文件dtsl_config_scripts.py中,定义的DtslScript类中OptionList对应DS5中DTSL Option的子菜单内容,如下图所示:
DTSL脚本的函数主要是几类,一个是初始化__init__(),顾名思义,初始化函数用于配置SoC的结构,并且对相对应的Debug和Trace方法进行配置。这个函数在DS5中的debug Configuration时被调用。在初始化中,最关键的函数就是discoverDevices(),从rvc的文件中获取每个debug和trace模块的信息,并且组建整个SoC中debug和trace的拓扑结构,这个部分也是在生成DTSL脚本文件中,由于假设和实际情况不符,最容易出现错误的部分。
另外一类是获取配置页面的选项值的回调函数optionValuesChanged(),主要是对DTSL Options配置菜单里面的选项值修改进行配置。
还有就是一些和目标平台独立无关的函数,比如addManagedPlatformDevices()等,各个SoC平台是类似的。
由于DTSL的脚本是工具自动生成的,在输入信息有限的情况下,会做出一些假设,从而导致一些错误的发生,不能正常进行debug和Trace功能。这里是常见的一些问题和修复方法。
由于DTSL的脚本是根据RVC里面的索引进行整个SoCdebug和trace拓扑结构进行配置的,如果索引错误,将会导致访问资源错乱。
一般的索引是直接读取的SoC的ROMTable,按照基地址从小到大的顺序进行索引。在SoC设计中,一般建议最外围的debug和trace放于低端地址,比如DAP中的TPIU,ETB/ETR等,然后是内部的Trace链,比如Funnel,把debug和trace源置于相对高端地址。
在SoC没有准备好的情况下,涉及到手动进行RVC的配置,或者FPGA中进行不完整芯片的仿真,更要特别需要注意debug和trace的索引顺序。
当SoC中有多个Cluster时,需要多个CSFunnel连接多个Debug和Trace的源,而CSFunnel的逻辑结构在RVC文件中是缺少描述的,在生成DTSL脚本文件的时候,默认是多个CSFunnel串行的,需要修改成真正的拓扑结构,比如典型的big.LITTLE的双cluster SoC如下图所示:
设计的修改除了在discoverDevices()中修改逻辑结构外,还需要特别注意CSFunnel的端口的配置和使能,比如在enableFunnelPortForSource()中,对debug和trace的source端口和CSFunnel的索引进行配置。下面是典型修改的示例:
Always enable all port in funnel0 in discoverDevices()
# Funnel 0
funnelDev0 = self.findDevice("CSTFunnel")
self.funnel0 = self.createFunnel(funnelDev0, "Funnel_0")
self.funnel0.setPortEnabled(0)
self.funnel0.setPortEnabled(1)
Add get funnel function:
def getFunnelForSource(self, source):
'''Get the funnel number for a trace source'''
# Build map of sources to funnel ports
funnelMap = {}
for i in range(0, NUM_CORES_CORTEX_A7):
funnelMap[self.ETMs[i]] = self.funnel1
for i in range(0, NUM_CORES_CORTEX_A15):
funnelMap[self.PTMs[i]] = self.funnel2
return funnelMap.get(source, None)
Correct the funnel port in enableFunnelPortForSource()
def enableFunnelPortForSource(self, source, enabled):
'''Enable/disable the funnel port for a trace source'''
port = self.getFunnelPortForSource(source)
funnel = self.getFunnelForSource(source)
if enabled:
funnel.setPortEnabled(port)
else:
funnel.setPortDisabled(port)
和CSFunnel类似,由于在RVC文件中缺少CSCTI的连接逻辑信息,在生成DTSL脚本的时候,对CSCTI的映射也做了相应的假设。特别是在SoC中,CSCTI有很多个,需要仔细查看每个CSCTI对应的索引,以及在DTSL脚本中的映射位置。
这部分问题主要需要对应DTSL脚本中discoverDevices()函数中对所有CTI的处理,和实际SoC中的CTI的使用手动对比,确保映射关系的正确性。
如果芯片中有多个Cluster,一般DTSL默认每个Cluster是四个设备,如果实际的SoC中每个Cluster只有两个设备,此时SMP的对应控制是错误的,需要根据实际的芯片情况进行DTSL脚本进行修改。
由于DTSL的脚本是基于Jythong的,非常容易进行功能的增添来实现一些特定的功能。比如SoC中既有Cortex-M系列核,又有Cortex-A的应用核,如果需要实现在MCU暂停的情况下,同时暂停Cortex-A系列主核,由于在DTSL中提供了CSCTI的类,我们不需要详细了解CTI的寄存器,就可以通过调用类中的函数很容易实现这类功能。
类似的,如果需要调试DTSL,需要输出一些log到文件中,可以直接定义一个log函数如下:
def log(message):
f = open("c:/temp/tmp.log", "at")
f.write(message + "\n")
f.close()
在需要添加log的函数中直接调用此函数输出log到文件中:
log("Tracecapture name %s" % traceCapture.getName())
log("manage device for list0 %s %s" % (str(traceMode_0), c.getName()) )
SoC的配置文件配合DTSL的脚本,实现了灵活多变的SoC的debug和trace平台配置。并且基于Jython的脚本,可以很容易的发现和修改实际SoC的配置问题,不啻为复杂SoC芯片开发的利器。