安森美RSL10开发-DSP
软件工具资料下载
注册并下载AISP工具,获取临时授权30天,需要公司邮箱注册,要有公司网站。
相关文档:
RSL10 LPDSP32 Support Manual.pdf
user_guide_lpdsp32_v3.pdf
授权
获取到临时授权后下载并安装相关软件及密钥:
1、ASIP_Programmer_vQ-2020.03_win.exe
2、scl_v2018.06_windows.exe
3、Synopsys_Temp_Key_Site_NEWSITE_snpslmd.lic 由txt后缀更名为lic
4、asip_installation_manual.pdf
建议软件安装目录不要更改!默认即可
LMTOOL设置如下:
环境变量设置如下:
计算机名称如下:
License修改如下:
LMstat的命令输出:
对于授权的延期,可能需要删除目录<C:\Synopsys\SCL\2018.06\admin\license>下的*.lic文件,然后重新启动lmtool中的服务
有时会出现打不开软件提示无授权,这时需要重新启动下LMTOOL工具中的服务!
调试
为了方便调试增加两个环境变量
- JTalk.exe的路径:C:\Synopsys\ASIP Programmer\Q-2020.03\win64\bin\WINbin
- JLinkARM.dll动态库的路径:C:\Program Files\SEGGER\JLink_V750
需要下载jlink的工具点我
仿真调试
连接硬件调试
调出cmd命令行,键入:
jtalk.exe -c jlink -f 2000
启动类似于GDB Server
打开ChessDE ASIP Programmer Q-2020.03
进行调试。
编译
点击Make
大致输出如下信息:
bridge -oRelease/DSP_Algorithm Release/main.o Release/CircularQueue.o Release/lpdsp32_init.o -IE:/AISP_CHESSDE_Workspace/AISP_TOOL/lpdsp32-v3_vQ-2020.03_windows/lpdsp32-v3_vQ-2020.03/lib -IE:/AISP_CHESSDE_Workspace/AISP_TOOL/lpdsp32-v3_vQ-2020.03_windows/lpdsp32-v3_vQ-2020.03/lib/isg -g -IUtils -IE:/AISP_CHESSDE_Workspace/AISP_TOOL/lpdsp32-v3_vQ-2020.03_windows/lpdsp32-v3_vQ-2020.03/lib/runtime/include -D__tct_patch__=0 -cDSP_Algorithm.bcf -LE:/AISP_CHESSDE_Workspace/AISP_TOOL/lpdsp32-v3_vQ-2020.03_windows/lpdsp32-v3_vQ-2020.03/lib -LE:/AISP_CHESSDE_Workspace/AISP_TOOL/lpdsp32-v3_vQ-2020.03_windows/lpdsp32-v3_vQ-2020.03/lib/runtime/lib -LE:/AISP_CHESSDE_Workspace/AISP_TOOL/lpdsp32-v3_vQ-2020.03_windows/lpdsp32-v3_vQ-2020.03/lib/softfloat/lib -llpdsp32 -lc -lsoftfloat -lm -a2 -m -fH +work Release/chesswork -plpdsp32
darts -IE:/AISP_CHESSDE_Workspace/AISP_TOOL/lpdsp32-v3_vQ-2020.03_windows/lpdsp32-v3_vQ-2020.03/lib +p -d -IUtils -IE:/AISP_CHESSDE_Workspace/AISP_TOOL/lpdsp32-v3_vQ-2020.03_windows/lpdsp32-v3_vQ-2020.03/lib/runtime/include -D__tct_patch__=0 +Mhex +Ihex -g +u Release/DSP_Algorithm lpdsp32
调试
再点击start debugging
应用开发
DSP与M3通讯两种方式:共享内存和中断,所以了解熟悉中断的配置,和链接文件是前提。
连接文件lpdsp32.bcf
,位于目录C:\Synopsys\ASIP Programmer\Q-2020.03\lpdsp32-v3_vQ-2020.03_windows\lpdsp32-v3_vQ-2020.03\lib
DSP中断向量表
软件触发中断方式
不可设置IMSK or IE寄存器来屏蔽中断!
//Example operation: sint 0, sint 2 // even vector table address 0, 2, 4, 6, 8….30
//To trigger sint software interrupt instruction
inline assembly int software_interrupt()
clobbers() property(volatile functional loop_free)
{
asm_begin
sint 4; nop /*software interrupt */
asm_end
}
// file lpdsp.bcf
// central link file with default link constraints
_symbol _ivt 0 // interrupt vector table at PM 0
_entry_point _ivt
_symbol _main_init _after _ivt
_symbol _main _after _main_init
_stack DMA 0xe000 8184 // stack region in DMA (start_address size).
// SP is initialised to (start_address + size), which
// must be multiple of 8 (to have aligned long access).
_exclusive DMA 0x0 1 // NULL pointer
_exclusive DM 0xC00000 4 // memory location used by OCD debug client for register read/write
// include to use main(argc,argv) arguments
_always_include _main_argv_area
DSP部分的一小段测试程序
/* ----------------------------------------------------------------------------
* Copyright (c) 2017 Semiconductor Components Industries, LLC (d/b/a
* ON Semiconductor), All Rights Reserved
*
* This code is the property of ON Semiconductor and may not be redistributed
* in any form without prior written permission from ON Semiconductor.
* The terms of use and warranty for this code are covered by contractual
* agreements between ON Semiconductor and the licensee.
*
* This is Reusable Code.
*
* -----------------------------------------------------------------------------
*/
#include <stdlib.h>
#include <stdint.h>
// Define the location of the CSS command register, this would normally be
// taken from a standard include file however to provide clarity in this
// example we will define it explicitly
#define CSS_CMD 0xC00004
// Define the specific interrupt that the ARM will trigger when it wants
// us to perform a calculation
#define CSS_CMD_0 (1<<0)
// Define the shared memory between the ARM and LPDSP processors
uint32_t chess_storage(DMB:0x00803800) sharedMemory[2];
// Define the interrupt register to notify the ARM of a completed operation
volatile static unsigned char chess_storage(DMIO:CSS_CMD) CssCmdGen;
// handling requests from the CM3 is activated
static volatile int actionRequired;
/* ----------------------------------------------------------------------------
* Function : isr0
* ----------------------------------------------------------------------------
* Description : Interrupt service routine invoked on request from the CM3
* Inputs : None
* Outputs : sets flag indicating work needs to be done
* Assumptions : None
* ----------------------------------------------------------------------------
*/
extern "C" void isr0() property (isr) {
// raise the flag indicating something needs to be processed
actionRequired = true;
}
/* ----------------------------------------------------------------------------
* Function : fibonacci
* ----------------------------------------------------------------------------
* Description : calculate the nth fibonacci number where:
* 0 is 0,
* 1 is 1,
* 2 is 1,
* 3 is 2, etc
*
* Inputs : nth, the number of the fibonacci sequence to return
*
* Outputs : the fibonacci number as defined above, or 0xFFFFFFFF if overflow
*
* Assumptions : None
* ----------------------------------------------------------------------------
*/
uint32_t fibonacci(uint32_t nth) {
// we know the 48th fibonacci number will be too large for a uint32_t
if (nth >= 48)
return 0xFFFFFFFF;
// deal with trivial cases
if ((nth == 0) || (nth == 1))
return nth;
// otherwise calculate the return value in a loop
uint32_t value = 0, first = 0, second = 1;
while (nth-- > 1) {
value = first + second;
first = second;
second = value;
}
return value;
}
/* ----------------------------------------------------------------------------
* Function : main
* ----------------------------------------------------------------------------
* Description : The main entry point for the program
*
* Note: this halts the core when not busy and is awoken by an interrupt
* from the CM3
*
* Inputs : None
*
* Outputs : returns zero but the code is not expected to ever complete
*
* Assumptions : None
* ----------------------------------------------------------------------------
*/
extern "C" int main(void) {
// assume nothing ready to be processed yet
actionRequired = false;
// enable the interrupts
enable_interrupts();
// spin forever, waiting for interrupts from the CM3
while (true) {
core_halt();
// only do something if we are responding to an interrupt from the CM3
if (actionRequired == true) {
actionRequired = false;
// calculate the nth fibonacci number
sharedMemory[1] = fibonacci(sharedMemory[0]);
// notify the CM3 that we have completed processing
CssCmdGen = CSS_CMD_0;
}
}
return 0;
}
DSP通知 M3内核触发中断,方式:
volatile static unsigned char chess_storage(DMIO:CSS_CMD) CssCmdGen;
#define CSS_CMD_0 (1<<0)
CssCmdGen = CSS_CMD_0;M3通知DSP触发中断,方式:
/* Tell the DSP there is data available to deal with. */
SYSCTRL_DSS_CMD->DSS_CMD_1_ALIAS = DSS_CMD_1_BITBAND;
DSP汇编中的中断向量表:
定义一个中断服务函数
在默认的初始化代码中,由于没有定义中断,任何硬件/软件中断都会从ISR向量返回。
定义一个中断:
- 在中断向量表中使用jp
- 使用这个标签来描述这个中断。
.text global 0 _main_init
r = 1 // enable rounding
s = 1 // enable saturation
sp = _sp_start_value_DMA // init SP (adjusted to stack in lpdsp.bcf)
ie = 1 ; nop // enable interrupts
// area to load main() arguments
.bss global 0 _main_argv_area DMA 256
//step 1
.undef global text isr0
// the interrupt vector table with 15 interrupts
.text global 0 _ivt
jp _main_init // 0 - reset
reti ; nop // 2 - interrupt 1
reti ; nop // 4 - interrupt 2
reti ; nop // 6 - interrupt 3
reti ; nop // 8 - interrupt 4
reti ; nop // 10 - interrupt 5
reti ; nop // 12 - interrupt 6
reti ; nop // 14 - interrupt 7
reti ; nop // 16 - interrupt 8
reti ; nop // 18 - interrupt 9
jp isr0 // 20 - interupt 10 mapped to ISR from ARM
reti ; nop // 22 - interrupt 11
reti ; nop // 24 - interrupt 12
reti ; nop // 26 - interrupt 13
reti ; nop // 28 - interrupt 14
reti ; nop // 30 - interrupt 15
在C文件中添加如下:
extern "C" void isr0(void) property(isr)
{
}
链接文件设置
共享内存
M3内存地址:0x20011800
,对应DSP内存区域:0x00803800 in the DMB memory space
我们可以指定变量内存地址,共用一块内存。
PRAM区域:40KB大小
DSP_DRAM区域:48KB大小 0x0080 0000 - 0x0080 C000(DSP中的地址)
共享内存区域:2KB大小 0x0080 3800 + 0x0080 4000(DSP中的地址)
中断
lpdsp32_init.s
中断向量表修改如下:
// initialisation before entering the main function.
.text global 0 _main_init
r = 1 // enable rounding
s = 1 // enable saturation
sp = _sp_start_value_DMA // init SP (adjusted to stack in lpdsp.bcf)
ie = 1 ; nop // enable interrupts
// area to load main() arguments
.bss global 0 _main_argv_area DMA 256
.undef global text isr_DSS_CMD_0_BITBAND
.undef global text isr_DSS_CMD_1_BITBAND
.undef global text isr_DSS_CMD_2_BITBAND
.undef global text isr_DSS_CMD_3_BITBAND
.undef global text isr_DSS_CMD_4_BITBAND
.undef global text isr_DSS_CMD_5_BITBAND
.undef global text isr_DSS_CMD_6_BITBAND
// the interrupt vector table with 15 interrupts
.text global 0 _ivt
jp _main_init // 0 - reset
reti ; nop // 2 - interrupt 1
reti ; nop // 4 - interrupt 2
reti ; nop // 6 - interrupt 3
reti ; nop // 8 - interrupt 4
reti ; nop // 10 - interrupt 5
reti ; nop // 12 - interrupt 6
reti ; nop // 14 - interrupt 7
reti ; nop // 16 - interrupt 8
jp isr_DSS_CMD_0_BITBAND // 18 - interupt 9 mapped to ISR from ARM
jp isr_DSS_CMD_1_BITBAND // 20 - interupt 10 mapped to ISR from ARM
jp isr_DSS_CMD_2_BITBAND // 22 - interupt 11 mapped to ISR from ARM
jp isr_DSS_CMD_3_BITBAND // 24 - interupt 12 mapped to ISR from ARM
jp isr_DSS_CMD_4_BITBAND // 26 - interupt 13 mapped to ISR from ARM
jp isr_DSS_CMD_5_BITBAND // 28 - interupt 14 mapped to ISR from ARM
jp isr_DSS_CMD_6_BITBAND // 30 - interupt 15 mapped to ISR from ARM
中断服务修改如下:
main.c
/* ----------------------------------------------------------------------------
* Function : isr_DSS_CMD_0_BITBAND
* ----------------------------------------------------------------------------
* Description : Interrupt service routine invoked on request from the CM3
* Inputs : None
* Outputs : sets flag indicating work needs to be done
* Assumptions : None
* ----------------------------------------------------------------------------
*/
extern "C" void isr_DSS_CMD_0_BITBAND() property (isr)
{
// raise the flag indicating something needs to be processed
}
/* ----------------------------------------------------------------------------
* Function : isr_DSS_CMD_1_BITBAND
* ----------------------------------------------------------------------------
* Description : Interrupt service routine invoked on request from the CM3
* Inputs : None
* Outputs : sets flag indicating work needs to be done
* Assumptions : None
* ----------------------------------------------------------------------------
*/
extern "C" void isr_DSS_CMD_1_BITBAND() property (isr)
{
// raise the flag indicating something needs to be processed
}
/* ----------------------------------------------------------------------------
* Function : isr_DSS_CMD_2_BITBAND
* ----------------------------------------------------------------------------
* Description : Interrupt service routine invoked on request from the CM3
* Inputs : None
* Outputs : sets flag indicating work needs to be done
* Assumptions : None
* ----------------------------------------------------------------------------
*/
extern "C" void isr_DSS_CMD_2_BITBAND() property (isr)
{
// raise the flag indicating something needs to be processed
}
/* ----------------------------------------------------------------------------
* Function : isr_DSS_CMD_3_BITBAND
* ----------------------------------------------------------------------------
* Description : Interrupt service routine invoked on request from the CM3
* Inputs : None
* Outputs : sets flag indicating work needs to be done
* Assumptions : None
* ----------------------------------------------------------------------------
*/
extern "C" void isr_DSS_CMD_3_BITBAND() property (isr)
{
// raise the flag indicating something needs to be processed
}
/* ----------------------------------------------------------------------------
* Function : isr_DSS_CMD_4_BITBAND
* ----------------------------------------------------------------------------
* Description : Interrupt service routine invoked on request from the CM3
* Inputs : None
* Outputs : sets flag indicating work needs to be done
* Assumptions : None
* ----------------------------------------------------------------------------
*/
extern "C" void isr_DSS_CMD_4_BITBAND() property (isr)
{
// raise the flag indicating something needs to be processed
}
/* ----------------------------------------------------------------------------
* Function : isr_DSS_CMD_5_BITBAND
* ----------------------------------------------------------------------------
* Description : Interrupt service routine invoked on request from the CM3
* Inputs : None
* Outputs : sets flag indicating work needs to be done
* Assumptions : None
* ----------------------------------------------------------------------------
*/
extern "C" void isr_DSS_CMD_5_BITBAND() property (isr)
{
// raise the flag indicating something needs to be processed
}
/* ----------------------------------------------------------------------------
* Function : isr_DSS_CMD_6_BITBAND
* ----------------------------------------------------------------------------
* Description : Interrupt service routine invoked on request from the CM3
* Inputs : None
* Outputs : sets flag indicating work needs to be done
* Assumptions : None
* ----------------------------------------------------------------------------
*/
extern "C" void isr_DSS_CMD_6_BITBAND() property (isr)
{
// raise the flag indicating something needs to be processed
}
M3发送通知,DSP触发中断
SYSCTRL_DSS_CMD->DSS_CMD_0_ALIAS = DSS_CMD_0_BITBAND;/**< 触发isr_DSS_CMD_0_BITBAND中断*/
集成固化
DSP执行地址为:0x00220000
size:40K
If we bind the LPDSP32 code to the Arm Cortex-M3 core executable in some way, we can utilize the Arm
Cortex-M3 core to perform the loading operation. The Arm Cortex-M3 core application would normally reside in flash,
so we can store the LPDSP32 program with the Arm Cortex-M3 core and then have the Arm Cortex-M3 core copy the
data over as part of the initialization stage. RSL10 also provides an efficient Flash Copier block which can be employed
by the Arm Cortex-M3 core to enable this.
通常我们将代码二进制保存到flash中,M3启动后进行加载dsp代码到dsp的执行地址。
从DSP编译生成的二进制文件中提取code与数据
首先确保已经安装python,我的版本Python 3.9.6
工具用python写的,键入以下命令安装 elf文件解析工具:
python -m pip install pyelftools
生成如下数据:
typedef struct
{
void *buffer;
uint32_t fileSize;
uint32_t memSize;
uint32_t vAddress;
} memoryDescription;
uint8_t __attribute__ ((section (".dsp"))) echo_dsp_DM_Lo_0[] = { };
uint8_t __attribute__ ((section (".dsp"))) echo_dsp_DM_Lo_8[] = { };
uint8_t __attribute__ ((section (".dsp"))) echo_dsp_DM_Lo_264[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, };
uint8_t __attribute__ ((section (".dsp"))) echo_dsp_DM_Lo_32760[] =
{ };
memoryDescription __attribute__ ((section (".dsp"))) echo_dsp_DM_Lo_SegmentList[] =
{
/*buffer, fileSize, memSize, vAddress*/
{ echo_dsp_DM_Lo_0, 0x00000000, 0x00000008, 0x00000000 },
{ echo_dsp_DM_Lo_8, 0x00000000, 0x00000100, 0x00000008 },
{ echo_dsp_DM_Lo_264, 0x00000010, 0x00000010, 0x00000108 },
{ echo_dsp_DM_Lo_32760, 0x00000000, 0x00008000, 0x00007ff8 },
};
typedef struct
{
memoryDescription *entries;
uint32_t count;
} memoryOverviewEntry;
typedef struct
{
memoryOverviewEntry PM;
memoryOverviewEntry DMH;
memoryOverviewEntry DML;
} memoryOverview;
/*总览*/
memoryOverview echo_dsp_Overview =
{
/*memoryDescription ,count */
{ echo_dsp_PM_SegmentList, 3 },
{ echo_dsp_DM_Hi_SegmentList, 1 },
{ echo_dsp_DM_Lo_SegmentList, 4 }
};
使用方法
拷贝elfConvert
文件夹到DSP工程根目录,即与*.prx文件同级目录
# 新建auto目录后,执行如下命令
python elfConvert\elfConverter.py --elf Release\echo --output auto --tag echo_dsp
# 1、命令行中echo为编译生成的二进制文件名
# 2、echo_dsp是你对生成的文件,需要加的文件名前缀
# 3、auto指定生成的文件到auto目录下
对于脚本文件需要作下修改的,否则运行错误,其实写了修改过程的,但是操蛋的win11给我死机了,直接贴出来,自己用对比软件查看
DSPMemoryIntegrator.py文件
# ----------------------------------------------------------------------------
# Copyright (c) 2017 Semiconductor Components Industries, LLC (d/b/a
# ON Semiconductor), All Rights Reserved
#
# This code is the property of ON Semiconductor and may not be redistributed
# in any form without prior written permission from ON Semiconductor.
# The terms of use and warranty for this code are covered by contractual
# agreements between ON Semiconductor and the licensee.
#
# This is Reusable Code.
#
# -----------------------------------------------------------------------------
#
from struct import unpack
from DSPSegmentKey import DSPSegmentKey
# Define base and extent of the PM, DM_High and DM_Low memory areas
#
_PM_BASE = 0x00000000
_PM_TOP = 0x00200000
_DM_LOW_BASE = 0x00000000
_DM_LOW_TOP = 0x00010000
_DM_HIGH_BASE = 0x00800000
_DM_HIGH_TOP = 0x00804000
# Define the segment type tags used by the LPDSP32 ELF file to identify the
# Memory Map and Memory Name segments
#
_LPDSP32_SEGMENT_MEMORY_MAP = 0x70123456
_LPDSP32_MEMORY_NAMES = 0x70123457
class DSPMemoryIntegrator(object):
'''
This class provides the hooks to convert the memory segments stored
in an LPDSP32 ELF file into C header and source files which can be
incorporated into a CM3 program.
'''
def __init__(self, elf, output, tag, v1):
'''
Constructor
elf
An ELFFile object that has already parsed the original file into
parsable structures
output
The folder to which the generated files will be written
tag
A tag that is used when creating file and structure names so that
they are identifiable by the CM3
v1
Flag indicating of old style formatting should be handled.
'''
self.elf = elf
self.output = output
self.tag = tag
self._unpackUint32 = ">LL" if v1 else "<LL"
self._divisor = 3 if v1 else 1
self._createSegmentTypeMapping()
def _uint32Generator(self, segmentData):
''' generate a uint32_t item from packed data '''
for chunk in [segmentData[i:i+8] for i in range(0, len(segmentData), 8)]:
yield unpack(self._unpackUint32, chunk)
def _createSegmentMap(self, segmentData):
''' create a single segment mapping table '''
self._segmentMap = dict(pair for pair in self._uint32Generator(segmentData))
def _createSegmentTypeMapping(self):
'''
Construct a mapping table so we can recover the types of segments later
'''
self._segmentIndexMap = {}
for index in range(self.elf.num_segments()):
segment = self.elf.get_segment(index)
self._segmentIndexMap[DSPSegmentKey(segment.header, segment.data())] = index
if segment.header.p_type == _LPDSP32_MEMORY_NAMES:
self._segmentTypes = segment.data()
elif segment.header.p_type == _LPDSP32_SEGMENT_MEMORY_MAP:
self._createSegmentMap(segment.data())
def _segmentTypeString(self, index):
''' retrieve the segment type string '''
interesting = self._segmentTypes[index:].decode().split('\0')
return interesting[0]
def _segmentType(self, segment):
''' retrieve the type of LOADable segments '''
if segment.header.p_type == 'PT_LOAD':
segIndex = self._segmentIndexMap[DSPSegmentKey(segment.header, segment.data())]
segTypeIndex = self._segmentMap[segIndex]
return self._segmentTypeString(segTypeIndex)
return None
def _area(self, areaName):
''' helper routine defining an area name format '''
return "%s_%s" % (self.tag, areaName)
# When creating the memory overview header, we define the memoryOverview and
# memoryOverviewEntry using conditional compilation, this may be defined in
# multiple header files as each module is standalone. The conditional
# compilation ensures we don't get compilation errors if more than one
# header file is included.
def _createMemoryOverviewHeader(self, pmTag, dmhTag, dmlTag):
''' Create a memory overview file '''
define = "_%s_H_" % (self.tag)
header = "%s/%s.h" % (self.output, self.tag)
with open(header, "w") as output:
output.write("#ifndef %s\n" % define.upper())
output.write("#define %s 1\n" % define.upper())
output.write("\n")
output.write("/* Auto generated file\n")
output.write(" */\n")
output.write("\n")
output.write("#include <stdint.h>\n")
output.write("#include <utilities.h>\n")
output.write("\n")
output.write("#include \"%s.h\"\n" % self._area(pmTag))
output.write("#include \"%s.h\"\n" % self._area(dmhTag))
output.write("#include \"%s.h\"\n" % self._area(dmlTag))
output.write("\n")
output.write("#ifndef _MEMORY_OVERVIEW_DESCRIPTION_\n")
output.write("#define _MEMORY_OVERVIEW_DESCRIPTION_ 1\n")
output.write("\n")
output.write("typedef struct {\n")
output.write(" const memoryDescription *entries;\n")
output.write(" uint32_t count;\n")
output.write("} memoryOverviewEntry;\n")
output.write("\n")
output.write("typedef struct {\n")
output.write(" memoryOverviewEntry PM;\n")
output.write(" memoryOverviewEntry DMH;\n")
output.write(" memoryOverviewEntry DML;\n")
output.write("} memoryOverview;\n")
output.write("\n")
output.write("#endif\n")
output.write("\n")
output.write("extern USED memoryOverview %s_Overview SECTION(\".dsp\");\n" % self.tag)
output.write("\n")
output.write("#endif\n")
def _findSegments(self, tag, base, top):
'''
find all segments of the defined type, which are contained within the
bounds of the "base" and "top" indicators
'''
segments = []
for segment in self.elf.iter_segments():
if self._segmentType(segment) == tag:
if (segment.header.p_vaddr >= base) and (segment.header.p_vaddr < top):
segments.append(DSPSegmentKey(segment.header, segment.data()))
return segments
def _overviewSegments(self, area, suffix, base, top):
''' helper method to provide a single segment in a standard form '''
compositeName = area if suffix == '' else "%s_%s" % (area, suffix)
listName = "%s_%s_SegmentList" % (self.tag, compositeName)
segments = self._findSegments(area, base, top)
return " {%s, %d}" % (listName, len(segments)) if len(segments) > 0 else None
def writeOverviewSegments(self, output, text):
if text is None:
return
output.write("%s,\n" % text)
def _createMemoryOverview(self, pm, pmSuffix, dmh, dmhSuffix, dml, dmlSuffix):
''' Create the memory overview structure '''
source = "%s/%s.c" % (self.output, self.tag)
with open(source, "w") as output:
output.write("/* Auto generated file\n")
output.write(" */\n")
output.write("\n")
output.write("#include \"%s.h\"\n" % self.tag)
output.write("\n")
output.write("memoryOverview %s_Overview = {\n" % self.tag)
self.writeOverviewSegments(output, self._overviewSegments(pm, pmSuffix, _PM_BASE, _PM_TOP))
self.writeOverviewSegments(output, self._overviewSegments(dmh, dmhSuffix, _DM_HIGH_BASE, _DM_HIGH_TOP))
self.writeOverviewSegments(output, self._overviewSegments(dml, dmlSuffix, _DM_LOW_BASE, _DM_LOW_TOP))
output.write("};\n")
output.write("\n")
def _createMemorySource(self, areaName, suffix, generator):
'''
Create a C source file containing descriptions of each memory section
in the specified memory area.
'''
compositeName = areaName if suffix == '' else "%s_%s" % (areaName, suffix)
taggedName = self._area(compositeName)
source = "%s/%s.c" % (self.output, taggedName)
header = "%s.h" % taggedName
with open(source, "w") as output:
output.write("/* Auto generated file\n")
output.write(" */\n")
output.write("\n")
output.write("#include \"%s\"\n" % header)
output.write("\n")
for line in generator(areaName, taggedName):
output.write("%s\n" % line)
output.write("\n")
# When creating the memory header, we define the memoryDescription structure
# using conditional compilation, this may be defined in multiple header
# files as each module is standalone. The conditional compilation ensures
# we don't get compilation errors if more than one header file is included.
def _createMemoryHeader(self, areaName, suffix, generator):
'''
Create a header file describing the contents of the C file associated
with the given memory space.
'''
compositeName = areaName if suffix == '' else "%s_%s" % (areaName, suffix)
taggedName = self._area(compositeName)
header = "%s/%s.h" % (self.output, taggedName)
define = "_%s_H_" % (taggedName)
with open(header, "w") as output:
output.write("#ifndef %s\n" % define.upper())
output.write("#define %s 1\n" % define.upper())
output.write("\n")
output.write("/* Auto generated file\n")
output.write(" */\n")
output.write("\n")
output.write("#include <stdint.h>\n")
output.write("#include <utilities.h>\n")
output.write("\n")
output.write("#ifndef _MEMORY_DESCRIPTION_\n")
output.write("#define _MEMORY_DESCRIPTION_ 1\n")
output.write("\n")
output.write("typedef struct {\n")
output.write(" const void *buffer;\n")
output.write(" uint32_t fileSize;\n")
output.write(" uint32_t memSize;\n")
output.write(" uint32_t vAddress;\n")
output.write("} memoryDescription;\n")
output.write("\n")
output.write("#endif\n")
output.write("\n")
output.write("extern USED const memoryDescription %s_SegmentList[] SECTION(\".dsp\");\n" % taggedName)
output.write("\n")
output.write("#endif\n")
def _highByte40(self, b):
''' fetch high byte of 40 bit encoded program word '''
return format(b, '#04x')
def _midByte40(self, b1, b2):
''' fetch middle byte of 40 bit encoded program word '''
return format((b1 & 0x0f) | ((b2 << 4) & 0xf0), '#04x')
def _lowByte40(self, b1, b2):
''' fetch low byte of 40 bit encoded program word '''
return format(((b1 >> 4) & 0x0f) | ((b2 << 4) & 0xf0), '#04x')
def _fortyBit(self, chunk):
''' fetches the next program word as a encoded 40 bit value '''
if len(chunk) != 6:
raise ValueError("Unexpected PM Size")
return " %s, %s, %s, %s, %s," % \
(self._highByte40(chunk[5]), self._highByte40(chunk[4]),
self._midByte40(chunk[3], chunk[2]),
self._lowByte40(chunk[2], chunk[1]), self._lowByte40(chunk[1], chunk[0]))
def _thirtyTwoBit(self, chunk):
'''
Fetches the next 32 bits of data, padding it if the remaining data
is not big enough
'''
while len(chunk) < 4:
chunk.append(0)
return ", ".join(format(b, '#04x') for b in chunk)
def _listSegments(self, tag, segments, divisor=1):
'''
Method to provide the data for each segment in a defined format for
the overview sections.
'''
if len(segments) > 0:
yield "USED const memoryDescription %s_SegmentList[] SECTION(\".dsp\") = {" % tag
for segment in segments:
yield " {%s_%s, 0x%08x, 0x%08x, 0x%08x}," % (tag, segment.vaddr, int(segment.filesz), int(segment.memsz), int(segment.vaddr / divisor) )
yield "};\n"
def _PMGenerator(self, areaName, tag):
'''
Generator for a given program memory segment
This will create the initial data array for each provided segment and
then an overview table which provides details of each memory area
in the file and how to access it.
'''
segments = self._findSegments(areaName, _PM_BASE, _PM_TOP)
for segment in segments:
yield "USED const uint8_t %s_%s[] SECTION(\".dsp\") = {" % (tag, segment.vaddr)
data = bytearray(segment.data)
for chunk in [data[i:i+6] for i in range(0, len(data), 6)]:
yield self._fortyBit(chunk)
yield "};\n"
for line in self._listSegments(tag, segments, self._divisor):
yield line
def _paddedAndAligned(self, data, address):
'''
This ensures that each data area is correctly aligned and padded to full
word boundaries. This is necessary to ensure the Flash Copier can be
effectively used by the CM3 when loading the data area.
'''
result = bytearray(data)
# If we don't start on a word boundary, insert bytes at the start
bytesToAdd = address & 3
while (bytesToAdd > 0):
result.insert(0, 0)
bytesToAdd -= 1
# if we don't end on a word boundary, append bytes at the end
while (len(result) & 3) > 0:
result.append(0)
return result
def _DMGenerator(self, tag, segments):
'''
Generator for a given data memory segment
This will create the initial data array for each provided segment and
then an overview table which provides details of each memory area
in the file and how to access it.
'''
# Process each segment in turn
for segment in segments:
yield "USED const uint8_t %s_%s[] SECTION(\".dsp\") = {" % (tag, segment.vaddr)
data = self._paddedAndAligned(segment.data, segment.vaddr)
for chunk in [data[i:i+4] for i in range(0, len(data), 4)]:
yield " %s," % self._thirtyTwoBit(chunk)
yield "};\n"
# Create the memory area overview for this type of memory
for line in self._listSegments(tag, segments):
yield line
def _DMLowGenerator(self, areaName, tag):
'''
Generator which provides the segment details for the high data memory
'''
segments = self._findSegments(areaName, _DM_LOW_BASE, _DM_LOW_TOP)
for line in self._DMGenerator(tag, segments):
yield line
def _DMHighGenerator(self, areaName, tag):
'''
Generator which provides the segment details for the high data memory
'''
segments = self._findSegments(areaName, _DM_HIGH_BASE, _DM_HIGH_TOP)
for line in self._DMGenerator(tag, segments):
yield line
def _createMemoryFiles(self, areaName, suffix, generator):
'''
Create the source and header files associated with a single memory area
'''
self._createMemorySource(areaName, suffix, generator)
self._createMemoryHeader(areaName, suffix, generator)
def parseMemoryAreas(self):
'''
This is the only public method in this class and it manages the
extraction process for each of the memory areas and then creates the
overview files
'''
self._createMemoryFiles('PM', '', self._PMGenerator)
self._createMemoryFiles('DM', 'Hi', self._DMHighGenerator)
self._createMemoryFiles('DM', 'Lo', self._DMLowGenerator)
self._createMemoryOverviewHeader('PM', 'DM_Hi', 'DM_Lo')
self._createMemoryOverview('PM', '', 'DM', 'Hi', 'DM', 'Lo')
新建utilities.h
文件,内容如下:
#ifndef UTILITIES_H
#define UTILITIES_H
#ifndef SECTION
#ifdef __CC_ARM /* ARM Compiler */
#define SECTION(x) __attribute__((section(x)))
#define USED __attribute__((used))
#define UNUSED_CODE __attribute__((unused))
#elif defined (__IAR_SYSTEMS_ICC__) /* for IAR Compiler */
#define SECTION(x) @ x
#define USED __root
#define UNUSED_CODE
#elif defined (__GNUC__) /* GNU GCC Compiler */
#define SECTION(x) __attribute__((section(x)))
#define USED __attribute__((used))
#define UNUSED_CODE __attribute__((unused))
#else
#error not supported tool chain
#endif /* __CC_ARM */
#endif
#endif
/******************************** End of file *********************************/
加入到M3的编译环境中,同时也加入以下生成的文件
命令执行完毕后生成如下文件:
# 位于auto目录下
echo_dsp.c
echo_dsp.h
echo_dsp_DM_Hi.c
echo_dsp_DM_Hi.h
echo_dsp_DM_Lo.c
echo_dsp_DM_Lo.h
echo_dsp_PM.c
echo_dsp_PM.h
M3中加载
官方提供了示例代码,flashCopier.c
、loader.c
static void g722PLCDSPInitialise(CODEC codec)
{
pBaseDSPCodec object UNUSED_CODE = getDSPCodec(codec);
SYSCTRL->DSS_CTRL = DSS_LPDSP32_PAUSE;
loadDSPMemory(&g722_plc_dsp_Overview);
SYSCTRL->DSS_CTRL = DSS_LPDSP32_RESUME;
}
编译注意事项
添加源文件
设置头文件路径
消除“__”符号内部保留不给使用问题
使用如下设置,当然这是官方不推荐的设置。
更多的设置使用参考chess_user-manual.pdf
文档
编程注意事项
指针的使用
因为双核通讯涉及到共享内存,所以操作指针尤其注意指针赋值和引用问题,《RSL10 LPDSP32 Support Manual》第16页,4.2.3 Shared Memory一章中提及。
For the purposes of this example, we need two words that are visible to both the Arm Cortex-M3 core and the
LPDSP32. We can choose any suitable locations, provided we take care to avoid other items that may be trying to share
the space. For now we will allocate the words at fixed locations that we know are not conflicting. From the Arm
Cortex-M3 core, we use two words based at address 0x20011800.These appear in the memory map of the LPDSP32 at location 0x00803800 in the DMB memory space.
对于相同的内存地址,在地址映射关系是不同的,分别有各自的地址,所以对使用同一个指针,去操作里面的数据是有问题的!
变量地址
DMA address range 0,8388607
DMB address range 8388608,12582911
链接文件bcf的修改
可查看官方文档
Entry point
# 方式一
_entry_point symbol
# 方式二
_entry_point addr
_entry_point 1000 // start execution at address 1000
_entry_point _ivt // take _ivt as start symbol (to be put on 1000).
Memory Regions
# 栈区域
_stack memory address-range
# 跳过堆栈对齐检查,不建议
_stack memory address-range _no_check
# 保留内存区域-指明不可自动分配占用
_reserved memory address-range
# 指明只读属性内存,代码,常量,和constmem段,对应于函数中需要的常量值池会分配到这里
# 可以使用 +w 链接器选项从只读内存区域排除constmem部分(以便将它们映射到可写内存)。
_rodata memory address-range
# 不是所有符号都适用于只读属性内存,链接会出现错误,除非配置文件中添加如下指令
_prefer_read_only
# 只有符号不可以映射到独占属性之外的内存,他才可以声明为独占属性内存,实现几种方式:它可以在区域内有一个固定的地址,或者它可以有一个地址范围,这个地址范围是排他范围的子范围,或者它可以分配给一个内存子范围别名,它完全被排他范围覆盖
_exclusive memory address-range
# .bss段的初始化,一般以零初始化,以其他值初始化,使用以下语句
_fill memory address-range fill-value
对常量存储地址的指定
_mapping _in_range _type .bss DMA 0x5000 .. 0x9800
_mapping _in_range _type .data DMA 0x5000 .. 0x9800
_mapping _in_range _type .rodata DMA 0x5000 .. 0x9800
Sort Symbols Before Mapping
# 默认情况下,符号会按照它们在object文件中出现的顺序映射到内存中(但请考虑§3.3中讨论的single_symbol_sections的影响)
# 从左到右处理命令行上指定的不同对象文件
# 命令行上指定的归档的对象文件部分在命令行上指定的对象文件之后被处理
# 归档的Object文件部分按照它们在归档中出现的顺序进行处理
# 归档按照命令行上从左到右指定的方式进行处理
# 在第一个过程中,所有文本符号的数据都被映射
# 在第二次传递中,所有空间(.bss)符号都被映射
# 每个符号都根据其大小和对齐约束进行映射
# 当然,由符号(映射)语句处理的符号是首先映射的
# To sort the symbols during mapping, the following configuration statements can be used:
_symbol_sort what how memory
# The what field must be one of:
alignment
alignment_only
mem_size
size
size_only
type
Symbol Addresses
使用_after
or_next
必须是同一块内存。
# 指定符号存放地址
_symbol symbol addr
# 如果一个已定义的函数符号或具有名称符号的对象符号存在于一个要链接的对象文件中,
# 该符号将被放置在该符号所属的内存地址addr处。
# 要把一个符号放在另一个符号之后,可以使用下面的语句:
_symbol symbol2 _after symbol1
# 如果名称为symbol2的全局函数符号或全局对象符号存在于要链接的对象文件中,
# 则该符号将直接放在名称为symbol1的符号之后。
# 要将一个符号放在前面_symbol行中指定的符号之后,而不重复前面_symbol行中符号的名称,请使用_next语句:
_symbol symbol _next
# 将符号放入一个内存区域范围
_symbol symbol address-range
# 将符号放入指定段中
_symbol symbol _in_segment segment
Symbol Alignment
带有名称符号的已定义函数符号或对象符号将被放置,以使指定的对齐约束得到遵守。对齐值的指定应该考虑到符号映射到的内存。例如,当4字节整数对象的对齐约束为4(在一个字节可寻址内存中)时,您可以将对齐约束增加为的倍数
链接器配置文件中的4,但不能以非4的倍数减少或增加。在后一种情况下,链接会因错误而停止。
_align symbol alignment
Symbol Wrapping
意义不大
# 封装符号两种
_wrap symbol
_real symbol
# 原始bcf
_symbol value_table 0x1000 (also when using range)
_symbol __real_value_table 0x1000 (also when using range)
_symbol value_table _after _ivt (also when using _next shorthand)
_symbol __real_value_table _after _ivt (also when using _next shorthand)
_symbol _ivt _after value_table (also when using _next shorthand)
_symbol _ivt _after __real_value_table (also when using _next shorthand)
# will be executed as:
_symbol __wrap_value_table 0x1000
_symbol value_table 0x1000
_symbol __wrap_value_table _after _ivt
_symbol value_table _after _ivt
_symbol _ivt _after __wrap_value_
符号引用
所有对这个__StackLimit 符号的引用将得到值0x9800 ,不会有任何对齐或自由空间检查。
# C文件引用符号:
extern int __StackLimit ;
# bcf文件定义符号
_symbol __StackTop 0xAFFF //0x9800 + 0x1800
_extern __StackTop
_symbol __StackLimit 0x9800 //Stack Base Addr 0x9800
_extern __StackLimit
将在.map
文件中找到如下:
External symbols:
DRAM_DSP_SHARE_ADDR = 0x803800
__StackLimit = 0x9800
__StackTop = 0xafff
__Vectors = 0x0
_ctors_end = 0x0
_ctors_start = 0x0
_dtors_end = 0x0
_dtors_start = 0x0
_sp_end_DMA = 0x9800
_sp_start_DMA = 0xb000
符号的重定义
所有引用symbol1 将被替换为symbol2,注意,在_wrap语句中使用的符号不能同时解析为其他符号使用
_resolve symbol1 symbol2
不初始化区域定义
# 符号
_symbol symbol addr _no_init
_symbol symbol2 _after symbol1 _no_init
_symbol symbol _next _no_init
_symbol symbol address-range _no_init
# 内存地址
_no_init_range memory address-range
# 段
_segment segment memory address-range _no_init
_overlay_segment segment1 segment2 ... _no_init
指定内存大小
_mem_size memory size
段的分配
Either the memory region is specified explicitly in the BRIDGE configuration file:
_segment segment memory address-range
or when omitting the end address, the end address is determined during linking:
_segment segment memory start-addr
To place segments after previously mapped segments or overlay segments, use the _next keyword:
_segment segment memory _next
To place segment at the end of all mapped symbols, use the _last keyword:
_segment segment memory _last
设定共享内存区域
Shared Memory Regions
_shared memory1 address-range1 memory2 address-range2 ...