chunjiu 发表于 2021-4-6 15:44:40

探究 51 模拟器指令解码的效率问题

在做完了初期的模拟器之后, 检测的效果不是太好.

在 STM32F429 上, 经过强优化的代码也只能将主频勉强维持在 300 KHz 左右,

这离预计的目标 1.2MHz 相距甚远.

按照兄弟们的提示, 在实际上 168MHz 的 STM32 上, 执行比大约是 560:1 ,效率太低了 ...

所以今天在彻底优化之前检查一下指令解码器部分的效率.

从 C 编译器给出的 arm 汇编指令文件来看, 基本上能知道效率低的原因了:

对 255 条 51 指令, switch 展开后使用了查表法, 所以基本上能直达解析段代码,

但紧跟着的 SFR 寄存器访问就不行了, 一长溜的 cmp 指令命中率极低,

而且对模拟 51 的寄存器操作是一大堆寄存器互相绕来绕去交换数据和运算,

所以才造成执行效率超低.

8002130:        f7fe bc6f         b.w        8000a12 <alu_instruction_decode+0x322>
8002134:        28e0              cmp        r0, #224        ; 0xe0
8002136:        f43f ae5f         beq.w        8001df8 <alu_instruction_decode+0x1708>
800213a:        28f0              cmp        r0, #240        ; 0xf0
800213c:        f041 8332         bne.w        80037a4 <alu_instruction_decode+0x30b4>
8002140:        4b56              ldr        r3,         ; (800229c <alu_instruction_decode+0x1bac>)
8002142:        4a55              ldr        r2,         ; (8002298 <alu_instruction_decode+0x1ba8>)
8002144:        7818              ldrb        r0,
8002146:        4e52              ldr        r6,         ; (8002290 <alu_instruction_decode+0x1ba0>)
8002148:        4b52              ldr        r3,         ; (8002294 <alu_instruction_decode+0x1ba4>)
800214a:        7010              strb        r0,
800214c:        7018              strb        r0,
800214e:        f996 3000         ldrsb.w        r3,
8002152:        f7fe bc5e         b.w        8000a12 <alu_instruction_decode+0x322>
8002156:        28d0              cmp        r0, #208        ; 0xd0
8002158:        f001 8666         beq.w        8003e28 <alu_instruction_decode+0x3738>
800215c:        d83d              bhi.n        80021da <alu_instruction_decode+0x1aea>
800215e:        2882              cmp        r0, #130        ; 0x82
8002160:        f001 8656         beq.w        8003e10 <alu_instruction_decode+0x3720>
8002164:        2883              cmp        r0, #131        ; 0x83
8002166:        d12a              bne.n        80021be <alu_instruction_decode+0x1ace>
8002168:        4a48              ldr        r2,         ; (800228c <alu_instruction_decode+0x1b9c>)
800216a:        4b4a              ldr        r3,         ; (8002294 <alu_instruction_decode+0x1ba4>)
800216c:        6811              ldr        r1,
800216e:        781a              ldrb        r2,
8002170:        7808              ldrb        r0,
8002172:        4e47              ldr        r6,         ; (8002290 <alu_instruction_decode+0x1ba0>)
8002174:        4050              eors        r0, r2
8002176:        7018              strb        r0,
8002178:        f996 3000         ldrsb.w        r3,
800217c:        f7fe bc49         b.w        8000a12 <alu_instruction_decode+0x322>
8002180:        2881              cmp        r0, #129        ; 0x81
8002182:        f041 8334         bne.w        80037ee <alu_instruction_decode+0x30fe>
8002186:        4b43              ldr        r3,         ; (8002294 <alu_instruction_decode+0x1ba4>)
8002188:        4a3f              ldr        r2,         ; (8002288 <alu_instruction_decode+0x1b98>)
800218a:        7818              ldrb        r0,
800218c:        7812              ldrb        r2,
800218e:        4e40              ldr        r6,         ; (8002290 <alu_instruction_decode+0x1ba0>)
8002190:        4310              orrs        r0, r2
8002192:        7018              strb        r0,
8002194:        f996 3000         ldrsb.w        r3,
8002198:        f7fe bc3b         b.w        8000a12 <alu_instruction_decode+0x322>

现在定下的优化目标是:

对 51 SFR 寄存器的访问也使用查表法, 实在用 C 不行就直接使用内嵌的 asm.

现在要解决 switch 的展开问题, 难道要建一个 64K 的巨表来避免 arm 编译器的强行展开吗?

我先写一个脚本, 展开 51 的巨集指令表试试.

dukelec 发表于 2021-4-6 17:01:56

10 年前,實際使用 STC 51 MCU 外接的是 12MHz 晶體,所以,爲了仿真更貼近真實,1.2M 不夠,應該要支持 12M 才行,加油。。。

Error.Dan 发表于 2021-4-6 17:23:16

把51全部的SFR写成结构体,位和字节用联合实现,然后...位域访问?

目测这里有cmp的原因还是因为深入到了指令内部,在根据寄存器地址做寻址,而51真正的精华不就是那一堆变态的寻址方式,直接用指令代表寻址方式然后立即数就是干

啥也不懂,乱说的哈~

chunjiu 发表于 2021-4-6 17:38:18

Error.Dan 发表于 2021-4-6 17:23
把51全部的SFR写成结构体,位和字节用联合实现,然后...位域访问?

目测这里有cmp的原因还是因为深入到了指令 ...

兄弟没说错, 问题的原因就在于寄存器组的定义比较混乱.

我现在正在调整访问方式, 全部放在一个数组内, 强行用指针和下标访问,

免得编译器使用中间寄存器互相交换数据, 那种方式效率太低了.

chunjiu 发表于 2021-4-6 17:41:25

dukelec 发表于 2021-4-6 17:01
10 年前,實際使用 STC 51 MCU 外接的是 12MHz 晶體,所以,爲了仿真更貼近真實,1.2M 不夠,應該要支持 12 ...

MCU Flash 的访问不是 0 等待, 所以难度很大啊 ... {:lol:}

测试过从一个中断响应回来, 要耽误很久的重刷指令数据时间.

amigenius 发表于 2021-4-6 17:45:43

兄弟可否参考一下FC模拟器,参考一下它的架构,那玩意,72M的M3也能全速模拟1.8M的6502。我没仔细研究过,乱说一通的。

amigenius 发表于 2021-4-6 17:48:24

兄弟该不会用switch来判断指令吧??我看很多模拟器都是用函数指针数组来解析。

honeybear 发表于 2021-4-6 17:51:23

不懂,做这个是用来干什么的

chunjiu 发表于 2021-4-6 18:06:10

amigenius 发表于 2021-4-6 17:48
兄弟该不会用switch来判断指令吧??我看很多模拟器都是用函数指针数组来解析。 ...

没错, 就是用它的. 但没想到 switch 的效率太低了.

刚开始时感觉对函数的调用在进出时开销有点大,

所以想用 switch 直接整成一个大通铺, 省去调用的开销,

现在看起来不是那么回事 ...

函数指针是现在准备做的事情, 不过还是在想办法绕过调用环节的开销,

如果是 6502 就简单多了, 就那几个固定的寄存器, 不像 51 这么麻烦.

chunjiu 发表于 2021-4-6 18:07:08

honeybear 发表于 2021-4-6 17:51
不懂,做这个是用来干什么的

一个硬件形式的 debuger, 模拟 8051 的运行.

amigenius 发表于 2021-4-6 19:44:14

chunjiu 发表于 2021-4-6 18:06
没错, 就是用它的. 但没想到 switch 的效率太低了.

刚开始时感觉对函数的调用在进出时开销有点大,


刚好相反,大量的条件判断,switch效率太低,开销太大。而使用用函数指针,您每个指令解析函数里面,不要使用局部变量,就不会带来额外的出入栈开销,总体开销很小的,而且开销也比较确定。

chunjiu 发表于 2021-4-6 20:48:56

amigenius 发表于 2021-4-6 19:44
刚好相反,大量的条件判断,switch效率太低,开销太大。而使用用函数指针,您每个指令解析函数里面,不要 ...

当时已经考虑到局部变量的开销问题了. 使用的全是全局变量.

但今天检查了全局变量的汇编代码后也不行, 它还是用几个寄存器来回倒腾地址和数据.

所以, 我后面的代码已经开始用全局数组保存模拟的寄存器组.

计划是固定用一个 arm 寄存器做数组的指针, 然后用下标来指定模拟的 51 寄存器内容,

反正就是禁止编译器自己折腾那些数据, 这样处理起来应该会效率比较高了.

amigenius 发表于 2021-4-6 22:21:30

chunjiu 发表于 2021-4-6 20:48
当时已经考虑到局部变量的开销问题了. 使用的全是全局变量.

但今天检查了全局变量的汇编代码后也不行,...

为了避免编译器开优化后把全局变量倒腾在寄存器,并且频繁出入栈,您可以试试把51的寄存器定义在CCM里,并且指定地址存放,并加volatile修饰。

arm_m0 发表于 2021-4-6 22:55:28

楼主精力可嘉啊,楼主搞这个是只是为了学习?

chunjiu 发表于 2021-4-6 23:18:06

amigenius 发表于 2021-4-6 22:21
为了避免编译器开优化后把全局变量倒腾在寄存器,并且频繁出入栈,您可以试试把51的寄存器定义在CCM里, ...

CCM 的缺点就是太小了,我正在看它的相关资料,研究一下将哪部分塞进去最合适。

中断必须要放进去的,因为之前的代码用定时器后,发现速度下降的厉害,估计是flash响应的时间太长。

还有几个很大的数据表,用来避免连环 if 的,不知道能不能塞的下。

看样子还要花不少脑筋的……

chunjiu 发表于 2021-4-6 23:19:10

arm_m0 发表于 2021-4-6 22:55
楼主精力可嘉啊,楼主搞这个是只是为了学习?

差不多是为了学习,准备开始用国产51了。

PICTURE 发表于 2021-6-16 16:16:22

这个用STM32芯片运行的仿真器如何导入51的HEX 文件?

chunjiu 发表于 2021-6-16 16:21:50

PICTURE 发表于 2021-6-16 16:16
这个用STM32芯片运行的仿真器如何导入51的HEX 文件?

之前是直接读入 RAM 的(通过串口或 SPI),然后解析并执行。

现在手上有其它工作,等工作忙完了再重新折腾它。

还要进行重大的架构调整,否则无法在 STM32 上做到高速仿真运行。
页: [1]
查看完整版本: 探究 51 模拟器指令解码的效率问题