本帖最后由 zhanzr21 于 2022-8-17 19:03 编辑
#申请原创#
上一篇文章中讲到DSP作为LKS32AT085的外设, 这个DSP还可以独立运行作为协处理器工作. 它与LKS32AT085的主核心CortexM0的区别如下:
1. 需要Cortex M0初始化代码和数据, 无法自动从Flash启动
2. 本身无中断, 可以引起Cortex M0的中断
3. 无Stack概念, 即DSP上运行的代码不能调用函数.
除此之外, 这个DSP属于凌欧的自主指令集, 目前是没有高级语言编译器的, 只有一个汇编器+模拟器的工具. 目前看来在上面开发程序还是比较费人工的, 希望厂家继续提高此处的开发者体验.
DSP程序运行基本模式
首先DSP中的代码不能自动启动, 需要Cortex M0初始化Code和data段. 一旦运行起来, Cortex M0即可以去做其他工作. 等DSP上的代码运行完了, 可以通过指令发起Cortex M0的外设中断, Cortex M0可以来取数据以及初始化下一次的运算.
这里以一个小例子来看看基本的DSP代码开发与使用: arctan和直角三角形求斜边长度的运算
DSP有一个数学函数, 输入直角三角形的两边(X,Y), 可以求取夹角以及斜边长度.
DSP作为外设
代码如下:
typedef struct str_arctan_result {
uint16_t arctan;
uint16_t mod;
} arctan_result;
typedef struct str_arctan_input {
uint16_t x;
uint16_t y;
} arctan_input;
arctan_result cpu_issue_arctan_mod(arctan_input input) {
DSP_SC &= (~BIT2);
DSP_CORDIC_X = input.x;
DSP_CORDIC_Y = input.y;
arctan_result res;
res.arctan = DSP_CORDIC_ARCTAN;
res.mod = DSP_CORDIC_MOD;
return res;
}
验证一下结果:DSP as a periph delta:0
2AAB, 2002
角度: 0x2AAB/0x7FFF = 0.33335367900631735 大约是π/3, 也就是60度.
斜边长: 0x2002 约等于0x1000的两倍. 可以看出是个30度,60度的直角三角形.
DSP单独运行
首先要写DSP上运行的代码
LDRDHI R3 R4 0x0
ARCTAN R5 R3 R4
# insert dummy inst to wait for arctan finish
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
# arctan is stored at 0x5 in dsp's data mem
STRWI R5 0x5
# module is stored at 0x6 in dsp's data mem
STRWI R6 0x6
IRQ
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
ADD R0 R0 R0
将以上代码保存为arctan_simple.code, 其中arctan_simple为工程名
再准备一个DSP的数据文件, 保存为arctan_simple.data, 内容如下:
其中0x10001BB6即为要运算的X,Y值. 0x00000000为对齐值, 实际使用中如果发现不补齐, 运算出问题. 原因文档中没有找到.
将上述两个文件放在模拟器相同的目录, 修改工程配置文件:
再运行模拟器(此处模拟器其实是模拟器+汇编器二合一).
$ ./LKS081DSP_Emulator.exe
LKS081 DSP Emulator v1.1
Released on 2020-3-9
Author: zhangwl@linkosemi.com
Copyright. 2020 LINKO Semiconductor Co.,Ltd. All Rights Reserved.
Current project is <arctan_simple>.
Reading in <arctan_simple.code>...
<arctan_simple.code> contains 24 asm instructions
Reading in <arctan_simple.data>...
<arctan_simple.data> contains 2 words
2022-08-17 11:38:53
<arctan_simple> Emulation is starting ...
Info: at Line 24: IRQ
IRQ generated and DSP halted when PC = 19.
<arctan_simple> Emulation Finished
3132us elapsed on Emulator.
24Cycles elapsed on DSP.
Final GPR snapshot is as below:
PC = 0x0000
R0 = 0x00000000
R1 = 0x00000000
R2 = 0x00000000
R3 = 0x00001000
R4 = 0x00001bb6
R5 = 0x00002aa0
R6 = 0x00001ff0
R7 = 0x00000000
请按任意键继续. . .
以上操作等于 汇编+模拟. 在同级目录下面可以看到output目录, 里面有模拟的日志和汇编出来的二进制代码.
二进制代码和上述.data文件拷贝到MCU的工程中, 就可以对DSP做初始化了.
Note: 对于同样的输入, 模拟器运行的结果与DSP作为外设时不一致.
0x2AAB vs 0x2AA00x2002 vs 0x1FF0
貌似误差很小, 其实是需要凌鸥的工程师重点看看, 误差从何而来.
打开output目录中arctan_simple.hex
0x0000f01c,
0x0000792b,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x0000b145,
0x0000b186,
0x0000e000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
这就是DSP要运行的二进制代码, 拷贝到MCU工程中去, 使之成为一个const的数组.
const uint32_t test_code_arctan_simple[] = {
0x0000f01c,
0x0000792b,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x0000b145,
0x0000b186,
0x0000e000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
};
另外上文的.data文件也需要类似处理:
const uint32_t test_data_arctan_simple[] = {
0x10001BB6,
0x00000000,
};
这一点实在想吐槽, 为啥生成代码时不能自动生成C语言的数组呢, 希望凌鸥的工程师能发发力, 把细节做好一点.
以下是MCU中初始化DSP的代码与数据的代码样例, 为简便没有使能DSP的中断, 使用了不断poll标志位的方式.
arctan_result dsp_issue_arctan_mod_poll(const uint32_t *code_ptr,
const uint16_t code_u32_len,
const uint32_t *data_ptr,
const uint8_t data_u32_len) {
volatile uint16_t arctan, mod;
uint32_t i;
// dsp data mem flush
for (i = 0; i < 64; i++) {
REG32(DSP_DATA_MEM_BASE + i * 4) = 0;
}
// dsp code mem flush
for (i = 0; i < 512; i++) {
REG32(DSP_CODE_MEM_BASE + i * 4) = 0;
}
// dsp data mem init
for (i = 0; i < data_u32_len; i++) {
REG32(DSP_DATA_MEM_BASE + i * 4) = data_ptr[i];
}
// dsp code mem init
// code length 200 half word
for (i = 0; i < code_u32_len; i++) {
REG32(DSP_CODE_MEM_BASE + i * 4) = code_ptr[i];
}
// Pause DSP
DSP_SC |= BIT1;
// Reset DSP PC
DSP_SC |= BIT2;
// Clear the IRQ flag
DSP_SC |= BIT0;
// Start DSP
DSP_SC = BIT0;
// wait until irq set and dsp paused
while ((BIT0) != (DSP_SC & (BIT0))) {
__WFI();
}
arctan_result res;
res.arctan = REG32(DSP_DATA_MEM_BASE + 5 * 4);
res.mod = REG32(DSP_DATA_MEM_BASE + 6 * 4);
return res;
}
来看看结果:
DSP standalone delta:1
2AAC, 2002
Note: 对于同样的输入, 模拟器运行的结果/DSP作为外设/DSP单独运行全部不一致.
0x2AAB vs 0x2AA0 vs 0x2AAC
0x2002 vs 0x1FF0 vs 0x2002
貌似误差很小, 但作者实在想知道究竟是哪个地方不精确呢.
希望官方工程师能注意到这些误差, 后面能做相应的更正.
DSP指令一览
如上所述, 这个DSP没有Stack的概念, 导致不能调用函数, 这个不算bug, 因为本身设计目标不是一个完整的内核, 而是辅助数**算的协处理器.
这里把它的指令集和与Cortex M0的指令集放在一起做一个简短的对比.
| Cortex M0指令集 | DSP指令集 | 整数运算 | ADCS | ADD | ADD | ADDI | ANDS | SUB | ASRS | ASR | CMN | ASRI | CMP | LSL | EORS | LSLI | LSLS | MAC | LSRS | MACI | MULS | DIV | ORRS | SAT | REV | SATI | REV16 | | REVSH | | RORS | | RSBS | | SBCS | | SUB | | SXTB | | SXTH | | TST | | BICS | | UXTB | | UXTH | | 三角函数 | | SIN_COS | | ARCTAN | 其他函数 | | SQRT | 数据存取 | ADR | LDRWI | LDM | LDRDHI | LDR | STRWI | LDRB | | LDRH | STRDHI | LDRSB | | LDRSH | | MOV | | MRS | | MSR | | PUSH | | POP | | STM | | STR | | STRB | | STRH | | 分支跳转 | B | JUMP | BX | JUMPI | | JLE | | JLEI | 控制 | BKPT | IRQ | WFI | NOP | WFE | | CPSID | | CPSIE | | DMB | | DSB | | ISB | | MVNS | | NOP | | SEV | | SVC | | 总体而言, 这个DSP内核缺乏Stack相关指令, 缺乏条件判断指令, 缺乏中断相关指令, 比起Cortex M0, 多了一些数学函数, 多了除法指令. 这也是符合本身的定位的.
本文完整的测试代码下载地址点这里.
|
此文章已获得独家原创/原创奖标签,著作权归21ic所有,未经允许禁止转载。
共1人点赞
|