考虑三种广泛应用于嵌入式软件的结构或组件的代码,这三种结构或组件分别是:状态机,循环缓冲器,队列。
状态的变化是基于输入的变化。
C语言实现的一个软件状态机——座位安全带控制器
- 需求:有人坐到座椅上,在规定时间内若没有系好安全带,就启动蜂鸣器。
- 输入:
- seat: 是否有人坐;
- belt: 是否系安全带;
- timer: 定长计时器;
- 输出:蜂鸣器。
- 状态:state: 机器当前状态
#define IDLE 0 #define SEATED 1 #define BELTED 2 #define BUZZER 3 switch (state) { case IDLE: if (seat) { state = SEATED; timer_on = TRUE; } break; case SEATED: if (belt) state = BELTED; else if (timer) state = BUZZER; break; case BELTED: if (!seat) state = IDLE; else if (!belt) state = SEATED; break; case BUZZER: if (belt) state = BELTED; else if (!seat) state = IDLE; break; } ```
固定大小
的缓冲区来保存当前数据,按时序移动缓冲区的头部,缓冲区的指针指向下一个采样数据被替换的位置。每添加一个采样就覆盖原先要剔除的数据,当指针移动到缓冲区的末端,则自动回到顶部。
C语言实现的循环缓冲区
#define CMAX 6 /* filter order */ int circ[CMAX]; /* circular buffer */ int pos; /* position of current sample */
- 变量pos保存当前采样位置,向缓冲区添加值时,变量位置移动
//向缓冲区中添加新值 void circ_update(int xnew) { /* compute the new head value with wrap around; the pos pointer moves from 0 to CMAX-1 */ pos = ((pos == CMAX-1) ? 0 : (pos+1)); /* insert the new value at the new head */ circ[pos] = xnew; }
- pos指向数组末尾时返回0。
//初始化函数 void circ_init() { int i; for (i=0; i<CMAX; i++) /* set values to 0 */ circ[i] = 0; pos=CMAX-1; /* startat tail so first element will be at 0 */ }
- 为了使第一个数据元素进入circ[0],设置pos指到数组末尾,再添加第一个元素前,它的值会变为0。
int circ_get(int i) { int ii; /* compute the buffer position */ ii = (pos - i) % CMAX; return circ[ii]; /* return the value */ }
for(i=0,y=0.0;i<N;i++) y+=x[i]*b[i];
//修改后的update函数,按照既定顺序将新值放入缓冲区 void circ_update(int xnew) { /* add the new sample and push off the oldest one */ /* compute the new head value with wraparound; the pos pointer moves from CMAX-1 down to 0 */ pos = ((pos == 0) ? CMAX-1 : (pos-1)); /* insert the new value at the new head */ circ[pos] = xnew; }
int fir(int xnew) { /* given a new sample value, update the queue and compute the filter output */ int i; int result; /* holds the filter output */ circ_update(xnew); /* put the new value in */ for (i=0, result=0; i<CMAX; i++) result += b[i] * circ_get(i); return result; }
int iir2(int xnew) { int i, aside, bside, result; //ZMAX-1 :最陈数据不适用 for (i=0, aside=0; i<ZMAX-1; i++) aside += -a[i+1] * circ_get(i); for (i=0, bside=0; i<ZMAX-1; i++) bside += b[i+1] * circ_get(i); result = b[0] * (xnew + aside) + bside; circ_update(xnew); /* put the new value in */ return result; }
数据在无法预料的时刻到达和离开,或者在某时刻有不同数目的数据到达
可以用队列来描述一个无条件程序模型
顺序语句
序列判决节点:描述的全部控制类型
数据流节点:一个完整的表示基本语句块的数据流图
分层表述形式
,可以对一个数据流CDFG进行扩展来展现完整的数据流图
- CDFG例子:选择分支
if (cond1) bb1(); else bb2(); bb3(); switch (test1) { case c1: bb4(); break; case c2: bb5(); break; case c3: bb6(); break; }
- CDFG例子:循环
//for循环 for (i=0; i<N; i++) loop_body(); //while循环 i=0; while (i<N) { loop_body(); i++; }
主要任务:
标签让编译器不用担心指令和操作数的位置
第一次扫描过程:
位置计数器(PLC)
中
标记值即为PLC当前值。
ADD r0,r1,r2 FOO EQU 5 BAZ SUB r3,r4,#FOO
示例:创建一个符号表
使用如下简单的ARM汇编代码ORG 100 label1 ADR r4, c LDR r0,[r4] label2 ADR r4,d LDR r1,[r4] label3 SUB r0,r0,r1
- 一开始的ORG语句告诉我们程序开始地址
- 处理下一条语句只需要移动PLC,
由于上一条语句是一个位操作没有内存值
,PLC的值不变
- 由于ARM指令是4个字长,所以PLC将每次增加4个单位
装载次序
由用户给定。给定了每个文件放入内存的次序和每个目标文件的长度,就可以计算出每个文件的开始地址可重入的
//不可重入的 int foo=1; Int task( ) { foo=foo+1; return foo; }
//可重入的 int task(int foo ) { foo=foo+1; return foo; }
可重定位的
编译=翻译+优化
// 编译调用过程的代码:y=p1(a,b,c,d,x) ldr r3, [fp, #-32] ; get x, the fifth parameter str r3, [sp, #0] ; put into p1()’s stack frame ldr r0, [fp, #-16] ; put a into r0 ldr r1, [fp, #-20] ; put b into r1 ldr r2, [fp, #-24] ; put c into r2 ldr r3, [fp, #-28] ; put d into r3 bl p1 ; call p1() mov r3, r0 ; move return value into r3 str r3, [fp, #-36] ; store into y in stack frame
a × b + 5 × ( c − d ) a\times b + 5\times (c-d) a×b+5×(c−d)
; operator 1 (*) ADR r4,a LDR r1,[r4] ADR r4,b LDR r2,[r4] MUL r3,r1,r2 ; operator 2 (-) ADR r4,c LDR r1,[r4] ADR r4,d LDR r5,[r4] SUB r6,r4,r5 ; operator 3 (*) MUL r7,r6,#5 ; operator 4 (+) ADD r8,r7,r3
ldr r2, [fp, #-16] ldr r3, [fp, #-20] mul r1, r3, r2 ; multiply ldr r2, [fp, #-24] ldr r3, [fp, #-28] rsb r2, r3, r2 ; subtract mov r3, r2 mov r3, r3, asl #2 add r3, r3, r2 ; add add r3, r1, r3 ; add str r3, [fp, #-32] ; assign
if (a+b > 0) x = 5; else x = 7;
遍历CDFG创建控制流代码
,一个有序的CDFG遍历如下:ADR r5,a LDR r1,[r5] ADR r5,b LDR r2,[r5] ADDS r3,r1,r2 BLE l3 ;<= LDR r3,#5 ADR r5,x STR r3,[r5] B stmtent l3 LDR r3,#7 ADR r5,x STR r3,[r5] stmtend ...
ldr r2, [fp, #-16] ldr r3, [fp, #-20] add r3, r2, r3 cmp r3, #0 ;test the branch condition ble .L3 ; branch to false block if <= mov r3, #5 ; true block str r3, [fp, #-32] b .L4 ; go to end of if statement .L3: ; false block mov r3, #7 str r3, [fp, #-32] .L4:
在编译时进行
,其它的则在运行时进行将二维数组的访问转化为对一维数组的访问
。即 a[i,j] 变成 a[i*M+j] ,其中 j 的最大值为 M-1struct { int field1; char field2; } mystruct; struct mystruct a, *aptr = &a;
// old int foo(a,b,c) { return a + b - c;} z = foo(w,x,y); //new z = w + x - y;
for (i=0; i<4; i++) a[i] = b[i] * c[i];
a[0] = b[0] * c[0]; a[1] = b[1] * c[1]; a[2] = b[2] * c[2]; a[3] = b[3] * c[3];
for (i=0; i<2; i++) { a[i*2] = b[i*2] * c[i*2]; a[i*2+1] = b[i*2+1] * c[i*2+1]; }
// old for (i=0; i<N; i++) a[i] = b[i] * 5; for (j=0; j<N; j++) w[j] = c[j] * d[j]; //new for (i=0; i<N; i++) { a[i] = b[i] * 5; w[i] = c[i] * d[i]; }
#define DEBUG 0 if (DEBUG) dbg(p1);
w = a + b; x = c + w; y = c + d;
w=a+b; x=c+d; y=x+e; z=a-b;
w=a+b; z=a-b; x=c+d; y=x+e;