嵌入式系统中的代码压缩

    在我们的某一个POWERPC系统中,通过总线挂接一片FPGA,和NorFlash等外设。以前我们一直是通过jtag烧写NorFlash的,速度很慢,操作也比较麻烦。现在有了PGA,tpu决定在里面放4k的代码,用于通过串口下载bootloader,并烧写到NorFlash中。这样生产时只要FPGA烧写工具就行了。
    后来,在实现时发现,4k的代码空间,放上各种功能后,已经不够了。只好把各种功能打个折,勉强塞了进去。现在,可以用xmodem协议下载bootloader,但只能直接执行,没有足够的代码空间写NorFlash了。其实,如果把4k的二进制文件,用zip等工具压缩,能压到原来的一半大小。我们也可以仿照lzss等算法,实现一个自己的压缩功能。

    首先,我们定义好我们的压缩方案:
    用一到两个bit做标志,描述数据流。标志凑齐一个字节就输出到数据流。
        比如:  flag data data ...... data flag data ...... data
    标志1 : 数据流中下一个字节是未压缩数据,直接输出。
    标志00: 表示一个短的前向匹配。下一个字节包含距离和长度:
            高2位是匹配长度,0-3对应2-5的匹配长度。
            低6位是匹配距离,0-63对应1-64的匹配位置。
    标志01: 表示一个长的前向匹配。下面两个字节包含距离和长度:
            高4位是匹配长度,0-15对应3-18的匹配长度。
            低4位和下一个字节是匹配距离,0-4095对应1-4096的匹配位置。

    根据这个方案,就可以开始写解压与压缩的实现了。实际测试发现,这个压缩方案,可以把PowerPC的代码,压缩到原来的65%左右。基本的硬件初始化和解压代码,大概用了512字节,剩余的3.5k空间,大概可以放5.3k的代码。这样以来,最初的设想完全可以实现了,而且很有很多的剩余空间备用。


    C语言实现的压缩代码:

static u8 *dst, *src;
static int dp, sp, fp;
static int flag, bits;

void put_flag(int bit)
{
	if(bits==8){
		if(c_debug) printf("flag %02x at %04x\n", flag, fp);
		dst[fp] = flag;
		fp = dp;
		dp += 1;
		bits = 0;
		flag = 0;
	}

	flag >>= 1;
	if(bit)
		flag |= 0x80;
	bits += 1;
}

int tlz_compress(u8 *dst_buf, u8 *src_buf, int src_len)
{
	int i, j, pp;
	int match_len, match_pos;

	dst = dst_buf;
	src = src_buf;
	sp = 0;
	fp = 0;
	dp = 1;
	bits = 0;
	flag = 0;


	while(sp<src_len){
		/* find match
		 * max windows: 1-4095 1-63
		 * max length: 3-18/2-5
		 */
		match_len = 1;
		match_pos = 1;
		pp = sp-4095;
		if(pp<0)
			pp = 0;
		for(i=pp; i<sp; i++){
			for(j=0; (j<18 && sp+j<src_len); j++){
				if(src[i+j]!=src[sp+j])
					break;
			}
			if(j>=match_len){
				match_len = j;
				match_pos = sp-i;
			}
		}

		if(match_len==1 || (match_len==2 && match_pos>63)){
			/* raw byte */
			put_flag(1);
			if(c_debug) printf("%04x raw: %02x\n", sp, src[sp]);
			dst[dp++] = src[sp++];
		}else{
			put_flag(0);

			if(match_len<6 && match_pos<64){
				/* short match */
				put_flag(0);

				if(c_debug) printf("%04x short: pos=%4d len=%2d\n", sp, match_pos, match_len);
				sp += match_len;

				match_pos -= 1;
				match_len -= 2;
				match_pos |= (match_len<<6);

				dst[dp++] = match_pos;

			}else{
				/* long match */
				put_flag(1);

				if(c_debug) printf("%04x  long: pos=%4d len=%2d\n", sp, match_pos, match_len);
				sp += match_len;

				match_pos -= 1;
				match_len -= 3;
				match_pos |= (match_len<<12);

				dst[dp++] = (match_pos>>8);
				dst[dp++] = (match_pos&0xff);
			}
		}

	}

	/* end of stream */
	put_flag(0);
	put_flag(0);
	dst[dp++] = 0xff;

	if(bits){
		flag >>= (8-bits);
		if(c_debug) printf("flag %02x at %04x\n", flag, fp);
		dst[fp] = flag;
	}

	return dp;
}


    C语言实现的解压代码:
int tlz_decompress(u8 *dst, u8 *src)
{
	int i, sp, dp;
	int flag, offset, len;
	u8 *pbuf;

	sp = 0;
	dp = 0;
	flag = 0x0100;

	while(1){
		if(flag&0x0100){
			flag = src[sp++];
			if(d_debug) printf("flag %02x at %04x\n", flag, sp-1);
			flag |= 0x00010000;
		}

		if(flag&1){
			/* raw byte */
			if(d_debug) printf("%04x raw: %02x\n", dp, src[sp]);
			dst[dp++] = src[sp++];
			flag >>= 1;
		}else{
			flag >>= 1;
			if(flag&0x0100){
				flag = src[sp++];
				if(d_debug) printf("flag %02x at %04x\n", flag, sp-1);
				flag |= 0x00010000;
			}
			offset = src[sp++];
			if(flag&1){
				/* 01: long format */
				len = offset>>4;
				len += 3;
				offset <<= 8;
				offset |= src[sp++];
				offset &= 0x0fff;
				if(d_debug) printf("%04x  long: pos=%4d len=%2d\n", dp, offset+1, len);
			}else{
				/* 00: short format */
				if(offset==0xff)
					break;
				len = (offset>>6);
				len += 2;
				offset &= 0x3f;
				if(d_debug) printf("%04x short: pos=%4d len=%2d\n", dp, offset+1, len);
			}
			flag >>= 1;
			pbuf = &dst[dp-offset-1];
			for(i=0; i<len; i++){
				dst[dp++] = pbuf[i];
			}
		}
	}

	return dp;
}


    PowerPC汇编实现的解压代码:

/* 
    int tlz_decomp(u8 *dst, u8 *src);

	r3: dst-1
	r4: src-1
	r5: flag
	r8: match offset
	r7: match len

*/

tlz_decomp:
	li	r5, 0x0100

_main_loop:
	andi.	r0, r5, 0x0100
	beq	1f
	lbzu	r5, 1(r4)
	oris	r5, r5, 0x0001
1:
	andi.	r0, r5, 0x01
	srawi	r5, r5, 1
	beq	_match
_raw_byte:
	lbzu	r6, 1(r4)
	stbu	r6, 1(r3)
	b	_main_loop

_match:
	andi.	r0, r5, 0x0100
	beq	1f
	lbzu	r5, 1(r4)
	oris	r5, r5, 0x0001
1:
	lbzu	r6, 1(r4)
	andi.	r0, r5, 0x01
	beq	_short_match
_long_match:
	srawi	r7, r6, 4
	addi	r7, r7, 3
	lbzu	r8, 1(r4)
	rlwimi	r8, r6, 8, 20, 23
	b	_copy
_short_match:
	cmpwi	r6, 0xff
	beqlr
	srawi	r7, r6, 6
	addi	r7, r7, 2
	andi.	r8, r6, 0x3f
_copy:
	srawi	r5, r5, 1
	sub	r8, r3, r8
	subi	r8, r8, 1
	mtctr	r7
1:
	lbzu	r6, 1(r8)
	stbu	r6, 1(r3)
	bdnz	1b

	b	_main_loop
    这里与C实现不一样的是,入口参数都要减一。这是为了充分利用PPC的指令特点,可以节省两条指令。这段代码占144字节空间。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值