我有一些旨在处理 1-256 字节的函数,在嵌入式 C 平台上运行,其中传递一个字节比传递一个 int 更快、更紧凑(一条指令对三条指令),它的首选编码方式是什么:
- 接受一个 int,如果为零则提前退出,否则将计数值的 LSB 复制到一个无符号字符并在 do {} while(--count); 中使用它 循环(参数值 256 将被转换为 0,但会运行 256 次)
- 接受一个无符号字符,如果为零则提前退出,并有一个 256 字节的特殊版本的函数(这些情况将提前知道)。
- 接受一个无符号字符,如果为零则运行 256 次。
- 有一个像上面这样的函数,但是通过表现为 (0-255) 和 (256 only) 的包装函数调用它。
- 具有与上述类似的函数,但通过行为为 (0-255) 和 (256 only) 的包装宏调用它。
预计系统繁忙时,函数的内循环大概占处理器执行时间的15%-30%;它有时用于少量字节,有时用于大字节。该函数使用的内存芯片具有每个事务的开销,我更喜欢让我的内存访问函数在内部执行 start-transaction/do-stuff/end-transaction 序列。
最有效的代码是简单地接受一个 unsigned char 并将参数值 0 视为执行 256 字节的请求,依靠调用者来避免任何意外尝试读取 0 字节。不过,这似乎有点危险。其他人是否在嵌入式系统上处理过此类问题?他们是如何处理的?
编辑 平台是一个PIC18Fxx(128K代码空间;3.5K RAM),连接到一个SPI闪存芯片;当预期更少时读取 256 字节可能会超出 PIC 中的读取缓冲区。写入 256 字节而不是 0 会损坏闪存芯片中的数据。如果不检查忙状态,PIC 的 SPI 端口被限制为每 12 个指令次一个字节;如果这样做会慢一些。典型的写事务除了要接收的数据外,还需要发送 4 个字节;读取需要一个额外的字节用于“SPI 周转”(访问 SPI 端口的最快方法是在发送下一个字节之前读取最后一个字节)。
编译器是 HiTech PICC-18std。
我一般都喜欢 HiTech 的 PICC-16 编译器。HiTech 似乎已经将他们的精力从 PICC-18std 产品转移到了编译时间更慢的 PICC-18pro 系列,似乎需要使用 3 字节“const”指针而不是 2 字节指针,并且有关于内存分配的自己的想法。也许我应该多看看 PICC-18pro,但是当我尝试在 PICC-18pro 的 eval 版本上编译我的项目时,它不起作用,我也没有弄清楚为什么——也许是关于可变布局的一些不同意我的 asm 例程——我一直在使用 PICC-18std。
顺便说一句,我刚刚发现 PICC-18 特别喜欢 do {} while(--bytevar); 尤其不喜欢 do {} while(--intvar); 我想知道编译器在生成后者时的“思维”是什么?
做 { 本地测试++; --lpw; } 而(lpw); 第2533章 第2534章 2535 0144A8 2AD9 incf fsr2l,f,c 第2536章 2537 0144AA 0E00 movlw 低 ?_var_test 2538 0144AC 6EE9 movwf fsr0l,c 2539 0144AE 0E01 movlw 高 ?_var_test 2540 0144B0 6EEA movwf fsr0h,c 2541 0144B2 06EE decf postinc0,f,c 2542 0144B4 0E00 移动 0 2543 0144B6 5AED subwfb postdec0,f,c 2544 0144B8 50EE movf postinc0,w,c 2545 0144BA 10ED iorwf postdec0,w,c 2546 0144BC E1F5 bnz l242
编译器加载一个指向变量的指针,甚至不使用 LFSR 指令(需要两个字),而是使用 MOVLW/MOVWF 的组合(需要四个字)。然后它使用这个指针进行递减和比较。虽然我承认 do{}while(--wordvar); 不能产生像 do{}while(wordvar--); 这样好的代码 该代码比后一种格式实际生成的要好。进行单独的递减和while-test(例如while (--lpw,lpw))会产生合理的代码,但看起来有点难看。后递减运算符可以为向下计数循环产生最佳代码:
decf _lpw btfss _STATUS,0 ; 如果进位则跳过下一个 inst(即不为零) decf _lpw+1 公元前循环; 只有当 lpw 为零时进位才会被清除
但它反而会生成比 --lpw 更糟糕的代码。最好的代码是向上计数循环:
infsnz _lpw incfsz _lpw+1 文胸环
但编译器不会生成它。
编辑 2 我可能使用的另一种方法:为字节数分配一个全局 16 位变量,并编写函数,使计数器在退出前始终归零。那么如果只需要一个 8 位的值,则只需要加载 8 位。我会使用宏来处理东西,以便可以调整它们以获得最佳效率。在 PIC 上,对已知为零的变量使用 |= 永远不会比使用 = 慢,而且有时更快。例如, intvar |= 15 或 intvar |= 0x300 将是两条指令(每种情况只需要处理结果的一个字节,而可以忽略另一个);intvar |= 4(或 2 的任何幂)是一条指令。显然在其他一些处理器上, intvar = 0x300 会比 intvar |= 0x300; 快 如果我使用宏,它可以适当地调整。