合泰半导体股份有限公司 技术讲座 - Holtek V3 C Compiler介绍 主讲人:王幼端 2017/06/15
前言 C语言已成为单片机程序开发的主要语言 C语言的效率普遍低于汇编语言 Holtek开发V3 C编译程序,具有强大的优化功能 编译程序由GNU C(GCC) V4.8.2移植 GCC默认使用GNU89 C标准 GNU89 = The 1990 C standard plus GNU extensions 本课程基于HT-IDE3000 V7.94及Holtek C Compiler V3.52编写
Holtek V3 C与各家编译器效率对比 评比结果 (数值越高,效率越高)
Holtek V3 C与Holtek旧版编译器对比 Holtek V2/V1转V3的实际案例 客户/MCU信息 旧版code size V3 code size 北京客户 HT56RB27(V2) ROM : 48K ROM : 40% RAM bank0 overflow ROM : 33% RAM bank0 : 剩余44byte 欧洲客户 HT66F017(V1) ROM : 2K ROM : 81% ROM : 72% USB FW程序 HT82K95A(V2) ROM : 4K ROM : 95% ROM : 65%
概述 语法介绍 编译器优化介绍 常见问题解决方式 参考文档 与标准C的语法区别 扩展及语法功能 自带的优化功能 实际优化案例 如何写出更有效率的代码 常见问题解决方式 参考文档
语法介绍
Holtek V3 C 与标准C的语法区别 V3 C不支援的标准C语法 功能差异 函数指针 (function pointer) 递归函数 (recursion) : V3不支持递归调用 (除了尾递归,编译器可以将它 优化成非递归函数) V3 C Compiler暂不支持MP只有7bit的MCU及特定MCU (HT45F39及HT45F391) 功能差异 V3支持部分的标准函数库 (string.h, math.h…) ,但不支持stdio.h (详见《Holtek C标准函数库使用手册》) 数据类型差异 V3 标准C float 3byte 4byte double 8byte
扩展语法及功能(1/14) V3代码生成器 选项路径: 工具 - V3代码生成器
扩展语法及功能(2/14) 中断服务程序 语法 范例 void __attribute__((interrupt(vector))) ISR_name (void) { } volatile unsigned char TM_5ms = 0; void __attribute__((interrupt(0x14))) ISR_STM (void) { TM_5ms ++; _t2af = 0; }
扩展语法及功能(3/14) 中断服务程序注意事项 既可以被中断服务程序访问,也可以被其他函数访问的全局变量应定义为volatile 中断入口会自动保存寄存器 (ACC, BP, STATUS, MP/TBLP…),并在中断出口时恢复 中断可以调用子函数 若中断与主程序调用同一个子函数(linker报出warning),需保证在执行这个子函数时不会进入中断,否则会导致执行错误
扩展语法及功能(4/14) 绝对地址变量 语法 范例 static volatile var_type vname __attribute__((at(addr))); static volatile unsigned char num1 __attribute__ ((at(0x180))); static volatile unsigned char tab[3] __attribute__((at(0x280)));
扩展语法及功能(5/14) 绝对地址变量注意事项 volatile用来修饰被不同程序访问和修改的变量 (比如同时被主程序和中断服务程序使用) 编译器会将之翻译成组合语言的EQU指令,如下: 绝对地址变量的作用域为本文档,需定义成static static volatile unsigned char num1 __attribute__((at(0x180))); num1 EQU [0180H]
扩展语法及功能(6/14) 定义const变量的地址 语法 范例 const ctype __attribute__ ((at(addr))) cname = number; const unsigned char __attribute__((at(0x700))) tab1[3]= {1,2,3};
扩展语法及功能(7/14) const变量的地址注意事项 需勾选以下参数才能使用 (路径 : 选项—工程设置—工程编译选项)
扩展语法及功能(8/14) 定义位变量 值只有0或1的变量可以定义成bit,占用1bit size,更节省空间 注意 : 全局的bit变量默认初始化,若不想初始化,可在工程设置中定义宏 : Disable_Bit_Initial V3.5 V3.4及之前的版本 bit flag1; bit flag2; typedef struct { unsigned char bit0 : 1; unsigned char bit1 : 1; unsigned char bit2 : 1; unsigned char bit3 : 1; unsigned char bit4 : 1; unsigned char bit5 : 1; unsigned char bit6 : 1; unsigned char bit7 : 1; }bit_type; bit_type bit_var; #define flag1 bit_var.bit0 #define flag2 bit_var.bit1
扩展语法及功能(9/14) 位变量注意事项 bit变量占用一个bit,仅最低位有效 bit变量不支持struct/union/const/register/array/pointer/函数参数及返回值 可指定bit变量的地址,语法如下: 范例 static volatile __attribute__((at(addr),bitoffset(val))) bit flag_name; static volatile __attribute__((at(0x18),bitoffset(0))) bit pa0; //表示pa0占用[18h].0的地址
扩展语法及功能(10/14) 内嵌汇编语言 语法 范例 说明 : compiler直接输出string字符串,可写在函数外,也可写在函数体内 asm(“string”); extern void FUN() ; asm(“extern _FUN_PAR1:byte”); //外部函式参数的声明 void main( ) { asm(“mov a,1”); asm(“mov _FUN_PAR1,a”); asm(“call _FUN”); //函数调用 }
扩展语法及功能(11/14) 指定函数的地址 语法 范例 中断函数也可以指定地址 : __attribute__((interrupt(vector),at(addr))) Re_type __attribute__((at(addr))) func (par1,par2) { } unsigned char __attribute__((at(0x700))) delay (unsigned int x) { }
扩展语法及功能(12/14) 支持硬件乘除法器 (MDU) 对具有乘除法寄存器的MCU,比如 : 个人医疗MCU BH66F2xx0/BH67F2xx0, V3.5 C compiler可利用MCU的硬件乘除法器,完成乘除法运算 以下是效益对比 : 乘除法运算库 MDU ROM Size (word) 运行时间 (最坏情况下的指令周期) 8bitх8bit 10 106 15 25 8bit / 8bit 133 16bitх16bit 19 289 29 16bit / 16bit 37 330 32bitх32bit 31 895 ─ 32bit / 32bit 67 1160 32bit / 16bit
扩展语法及功能(13/14) 硬件乘除法器 (MDU) 注意事项 大部分MCU的MDU都为16bit,所以对于char/unsigned char类型的乘除法运算,若使用MDU功能,使用的code size不一定更少,但运算速度会更快 不是所有的乘除法运算都会使用MDU运算 乘 long U long int U int char U char ─ MDU 除 long U long int U int char U char ─ MDU
少数ROM宽度<16位或者无TBHP的MCU 扩展语法及功能(14/14) 变量初始化 全局变量的初始化用startup code来实现,在main执行前完成初始化动作 (选项路径 : 选项→工程设置→编译选项→连接选项) 大部份MCU 少数ROM宽度<16位或者无TBHP的MCU 无勾选参数 startup1_l.asm startup1.asm 有勾选参数 startup0_l.asm startup0.asm
编译器优化介绍
编译器优化介绍 详见《Holtek C Compiler V3使用手册》 是否影 响调试 节省 ROM 节省RAM 节省 Stack 代数转换 (Algebraic Transformations) √ ● 复制传递 (Copy Propagation/Value Propagation) 删除执行不到的代码 (Unreachable code Elimination) 删除死代码 (Dead-code Elimination) 常量折迭 (Constant Folding) 常量传播 (Constant Propagation) 内联程序 (Inline Procedure) 强度削减 (Strength Reduction) 尾递归调用 (Tail Recursive Call) 子表达式删除 (Subexpression Elimination) 尾部合并 (Tail Merging) Bp 优化 Dead Section 删除
实际优化案例 优化案例 客戶/MCU信息 优化前 Code Size 优化后Code Size 南京客戶 HT45F67 ROM : 32K 北京客戶 HT69F50A ROM : 8K ROM : 81% ROM : 62% 台中客户 HT67F5650 ROM : 8K (长指令) ROM : 91% ROM : 83% 杭州客户 HT66F018 ROM : 4K ROM编译 Overflow ROM : 76%
如何写出更有效率的代码(1/8) 优化选项设置 (选项路径 : 选项 → 工程设置 → 编译选项) 编译设置 连接选项 优化生成的代码 (最优空间) 存取const变量时使用查表指令进行优化 连接选项 优化RAM空间 (不允许中断嵌套) 优化全局变量分配
如何写出更有效率的代码(2/8) 变量声明unsigned/singed 数据形态
如何写出更有效率的代码(3/8) 浮点常量
如何写出更有效率的代码(4/8) const数组
如何写出更有效率的代码(5/8) 循环替代
如何写出更有效率的代码(6/8) 封装频繁使用的代码为函数
如何写出更有效率的代码(7/8) 全局变量的分配
如何写出更有效率的代码(8/8) 中断服务程序 全局变量初始化 在中断里面实现的功能尽量少 减少复杂运算的使用 编译器调用startup函数为全局变量初始化,占用几十个word,如果需要 初始化的全局变量不多,可自己于函数中赋值
常见问题解决方式
常见问题解决方式(1/8) 编译器报的信息 Warning: variable 'temp' set but not used 这部分error/warning网站上都能查到大部分意思与解决方式 Internal compiler error… 编译器内部错误,请与Holtek反馈 Warning(L3008) : Same sub function exists between ISR(08H) CMG and ISR(04H) CMG: _func1 中断跟主函数或另一个中断调用同一个子函数func1 Error (L1038) "RAM (bank0) overflow, memory allocation fails for section …." RAM bank0溢出,可将全局变数用指定地址的方式定义至其它RAM bank
常见问题解决方式(2/8) 启用V3优化参数后,无法在Watch Window查看某些local变量数值? 因启用优化参数,变量有可能被优化,故无法查看 若要在debug时观察变量值,可以将此变量暂时定义为volatile, debug 结束后再删去,比如 : volatile int i,j,k; 中断访问的全局变量,此变量的相关语句被优化掉导致执行错误? 中断与主函数共同访问的全局变量需用volatile修饰
常见问题解决方式(3/8) 使用V3 compiler, debug时部分语句无法设置断点? 通常此类语句因无意义而被删除,如下line 4 因尾部合并进入错误的分支语句
常见问题解决方式(4/8) 使用V3 compiler,用于延时的循环代码被优化,怎么解决? 将变量定义成volatile,或使用内建延时函数GCC_DELAY (x),其中x为 unsigned int型常量
常见问题解决方式(5/8) 如何修改const变量的值? 比如,使用IAP把ROM的400H~410H值改写,执行temp=array[7]; temp值仍 为7 解决方式:把array[ ]定义在与temp = array[7];不同的C file __attribute__((at(0x400))) const unsigned char array[] = {0,1,2,3,4,5,6,7};
常见问题解决方式(6/8) 混合语言编程注意事项 使用长指令MCU,在C文件中定义全局变量若有在ASM文件中使用,需指 定地址或bank数 使用ASM写中断函数,应注意保存使用到的寄存器,特别是用到查表时,要 保存与恢复TBLH 具有多个ROM bank的MCU,在ASM文件中call function,应注意ROM BP的 设置与恢复,避免程序跑飞,如下 :
常见问题解决方式(7/8) 内嵌汇编的使用注意事项 汇编中使用的变量、函数、寄存器、标志位应符合汇编文件的定义 如果全局变量/函数在本文件中只在内嵌汇编中使用,应增加汇编的声明 : 寄存器/标志位应先定义后使用,可include INC文件,比如 : 内嵌汇编语言中的名字有区分大小写 asm(“extern _a:byte”); asm(“extern _func:near”); void main() { asm(“clr _a”); asm(“call _func”); } asm(“#include HT66F60.INC”); void main() { asm(“clr ACC”); asm(“clr C”); }
常见问题解决方式(8/8) 不要用汇编的思维写C程序,避免直接使用寄存器/标志位赋值,比如 : _mp1l = 0x80; _mp1h = 1; _iar1 = array[i]; 代替方案 : 定义变量var在地址0x180, var = array[i]; data <<= 1; if(_c) {…} 代替方案 : 直接判断data的最高位if (data&0x80) 编译程序用到的 特殊寄存器 主要用途 MP/IR 用于存取RAM space的值,搭配RAM BP使用,用于查表 BP RAM BP用于存取RAM space的值,ROM BP用于call function STATUS 用于表达式计算 TBLP 用于查表,用于存储RAM BP TBHP 用于查表 TBLH ACC 存储函数返回值等 PCL
参考文档 《HT-IDE3000使用手册》 《Holtek C Compiler V3使用手册》 《Holtek标准函数库使用手册》 介绍IDE, assembler, linker等tool的使用,在HT-IDE3000安装档DOC目录下 《Holtek C Compiler V3使用手册》 介绍Holtek C Compiler V3的使用,在HT-IDE3000安装档DOC目录下 《Holtek标准函数库使用手册》 介绍Holtek C支持的标准函数及使用方式,在HT-IDE3000安装档DOC目录下 《Holtek C Compiler V3 FAQ》 C Compiler V3的常见问题,在HT-IDE3000安装档DOC目录下 《gcc manual》 GCC使用手册下载网址http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc.pdf
问题与讨论 Q & A