ARM直到ARMv7才支持除法指令,支持情况如下:
Support for the SDIV/UDIV instructions is mandatory in ARMv7-M and for the Thumb instruction set in ARMv7-R. It is optional for the ARM instruction set in ARMv7-R. It is optional in ARMv7-A and, if supported, may be in the Thumb instruction set only or in both Thumb and ARM. In ARMv7-A with the Virtualization Extensions, it is mandatory in Thumb and ARM.
没有除法指令的架构,编译器通过实现一个除法函数,将除法编译为使用该函数实现,编译bare metal系统时,需要链接-lgcc。
但是,如果除法操作数全部是常数的话,编译器可以预先计算出结果,从而绕过除法指令或者调用除法函数,如下:
1 2 3 4 5 6 7 | #define PCLK_HZ (67*1000*1000) #define WATCHDOG_TIMER 5 #define prescaler 255 #define division_factor 128 count = PCLK_HZ / prescaler / division_factor * WATCHDOG_TIMER; |
如果某个操作数为变量,就需要使用运行在目标机器的除法实现了,如改成如下:
1 2 3 4 5 6 7 8 | #define PCLK_HZ (67*1000*1000) #define WATCHDOG_TIMER 5 #define prescaler 255 #define division_factor 128 int prescaler_2 = 255; count = PCLK_HZ / (1 + prescaler_2) / division_factor * WATCHDOG_TIMER; |
没链接gcc库,将报错:
1 2 3 4 5 | /work/toolchain/arm-unknown-linux-gnueabi/bin/arm-unknown-linux-gnueabi-ld -T bare.lds -o int_elf start.o init.o init.o: In function `start_watchdog': /work/code/s3c2440/watchdog_timer/init .c:130: undefined reference to `__aeabi_idiv' Makefile:3: recipe for target 'all' failed make : *** [all] Error 1 |
修改编译选项:
1 | $(LD) -T bare.lds -o int_elf start.o init.o /work/toolchain/arm-unknown-linux-gnueabi/lib/gcc/arm-unknown-linux-gnueabi/6.3.0/libgcc.a |
由于bare.lds未指定任何search dir,所以这里使用libgcc.a的绝对路径。
同时需要添加一个raise()函数,gcc库会调用该由用户实现的外部raise函数。
1 2 3 | void raise () { } |
可正常编译和烧录后运行正常,libgcc.a有22M之多,但是编译出来的bin很小,链接器只会链接静态库中用的模块,但是其是基于模块,而不是函数。
【1】Divide and Conquer. https://community.arm.com/processors/b/blog/posts/divide-and-conquer