众所周知,标准8051只有一个DPTR指针,通过DPTR访问XRAM速度是比较慢的,尤其是从XRAM的一个缓冲区搬数据到另外一个缓冲区,更是雪上加霜。
看到CH55x的头文件了提到了新增的A5指令,配合双DPTR指针,可以快速在XRAM中搬数据,写了段代码测试了下,从XRAM的的一个缓冲区搬255字节到另外一个缓冲区,对比对象是C标准库中的memcpy函数。同时由于CH55x的DPTR支持自增操作,把DPTR自增也作为一个变量一起测试一下。
结论如图:
mempcy函数:420us
A5指令不开自增:128us
A5指令开自增:128us
从测试结果来看,A5指令配合双DPTR速度提高了大约3.3倍,节省的时间还是相当可观的。DPTR自增对运行时间几乎无影响,按说循环中节省了一条INC指令,应该能提高一点速度呀,奇怪。
完整代码如下:
/*
New Instruction: MOVX @DPTR1,A
Instruction Code: 0xA5
Instruction Cycle: 1
Instruction Operation:
step-1. write ACC @DPTR1 into xdata SRAM embedded chip
step-2. increase DPTR1
ASM example:
INC XBUS_AUX
MOV DPTR,#TARGET_ADDR ;DPTR1
DEC XBUS_AUX
MOV DPTR,#SOURCE_ADDR ;DPTR0
MOV R7,#xxH
LOOP: MOVX A,@DPTR ;DPTR0
INC DPTR ;DPTR0, if need
DB 0A5H ;MOVX @DPTR1,A & INC DPTR1
DJNZ R7,LOOP
*/
extern UINT8X Ep1Buffer[];
extern UINT8X syscall_buf[];
BOOL VerifyMemory(void)
{
uint8_t i = 0;
for(i=0; i<0xFF; i++)
{
if(Ep1Buffer[i] != syscall_buf[i])
{
return FALSE;
}
}
return TRUE;
}
void testDualDPTR(void)
{
// memcpy
memset(syscall_buf, 0, 0xFF);
P33 = 1;
memcpy(syscall_buf, Ep1Buffer, 0xFF);
P33 = 0;
if(VerifyMemory())
{
#pragma ASM
SETB P33
NOP
CLR P33
#pragma ENDASM
}
// Dual DPTR without AUTO_INC
memset(syscall_buf, 0, 0xFF);
#pragma ASM
SETB P33
ORL XBUS_AUX, #01H ;DPS=1
MOV DPTR, #syscall_buf ;DPTR1
ANL XBUS_AUX, #0FEH ;DPS=0
MOV DPTR, #Ep1Buffer ;DPTR0
MOV R7,#0FFH
L0: MOVX A, @DPTR ;DPTR0
INC DPTR ;DPTR0++
DB 0A5H ;MOVX @DPTR1,A & INC DPTR1
DJNZ R7, L0
CLR P33
#pragma ENDASM
if(VerifyMemory())
{
#pragma ASM
SETB P33
NOP
CLR P33
#pragma ENDASM
}
// Dual DPTR with AUTO_INC
memset(syscall_buf, 0, 0xFF);
#pragma ASM
SETB P33
ORL XBUS_AUX, #05H ;bDPTR_AUTO_INC=1 DPS=1
MOV DPTR, #syscall_buf ;DPTR1
ANL XBUS_AUX, #0FEH ;DPS=0
MOV DPTR, #Ep1Buffer ;DPTR0
MOV R7,#0FFH
L1: MOVX A, @DPTR ;DPTR0
DB 0A5H ;MOVX @DPTR1,A & INC DPTR1
DJNZ R7, L1
ANL XBUS_AUX, #0FBH ;bDPTR_AUTO_INC=0
CLR P33
#pragma ENDASM
if(VerifyMemory())
{
#pragma ASM
SETB P33
NOP
CLR P33
#pragma ENDASM
}
}
离线
CH552的DPTR自增有坑,在USB传输时,USB的DMA可能修改DPTR用的bank,同时用户代码使用DPTR自增可能导致数据混乱。几个字节几十个字节测不出来,有时候几十KB都测不出来,但是有极其小的概率,大概几百KB一次的概率会触发。
离线
CH552的DPTR自增有坑,在USB传输时,USB的DMA可能修改DPTR用的bank,同时用户代码使用DPTR自增可能导致数据混乱。几个字节几十个字节测不出来,有时候几十KB都测不出来,但是有极其小的概率,大概几百KB一次的概率会触发。
那就不用它了,保持bDPTR_AUTO_INC一直为0,从测试结果来看,打开DPTR自增对加快复制速度也没有什么作用。
离线
@echo
对复制到非DPTR,比如外设,有用啊。
比如考虑如下场景:
__xdata uint8_t *buf;
int len;
while(len--)
{
SPI0_DATA=*buf++;
while(!S0_FREE);
}
这个代码可以优化成:
XBUS_AUX=0x00; // Select DPTR0
SAFE_MOD=*buf; // Load DPTR addresses from xdata pointer
SAFE_MOD=0x00; // Prevent 0x55, 0xaa patterns
XBUS_AUX=0x04; // Select DPTR0 and enable auto increment
while(len--)
{
__asm("movx @a, DPTR");
__asm("mov SPI0_DATA, @a");
while(!S0_FREE);
}
XBUS_AUX=0x00; // Disable DPTR auto increment
速度确实会快很多(SDCC操作DPTR默认每次都加载DPTR寄存器,非常保守,但是非常低效),但是每隔几百KB就会错乱一次,SPI收到的数据和USB发过来的对不上。
最近编辑记录 Blueskull (2021-10-22 22:50:28)
离线
@Blueskull
理论上会减少一条INC DPTR指令,时间应该能少一个周期,不过我原文实测的结果是没有差别。
离线
@Blueskull
理论上会减少一条INC DPTR指令,时间应该能少一个周期,不过我原文实测的结果是没有差别。
SDCC假设中断程序可能随时操作DPTR,因此生成的代码会每一次都重新加载DPTR寄存器。
// r6 is DPH, r7 is DPL
mov r5, r7
mov r4, r6
inc r5
// if(r5==0x00) r4++;
mov DPH, r4
mov DPL, r5
movx @a, DPTR
...
不知道为什么SDCC的xdata操作这么保守,但是确实可靠,就是慢。
离线
经过 @Alexi2008 大佬指点,找到了开不开地址自增运行时间一样都是128us的原因。
DJNZ指令如果位于奇地址,会增加一个周期,刚好吃掉INC DPTR指令省下来的那个周期。看了下汇编代码,确实开地址自增以后的DJNZ指令在奇地址。
改一下代码,让两条DJNZ指令都位于偶地址,再次测试,结果如下:
mempcy函数:420us
A5指令不开自增:128us
A5指令开自增:97us
可以看到,打开地址自增以后,运行时间从128us降低到了97us,提升也不少。极限情况下,A5指令可以比memcpy函数快4.33倍。
离线
给CH552洗白一下,CH552的DPTR自增没问题,我的代码有问题。今天把之前的codebase重构了一下,现在USB双缓冲+DPTR自增SPI回环+USB回环测试稳如老狗。
离线