在我們的某一個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語言實現的壓縮代碼: [cpp] 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語言實現的解壓代碼: [cpp] 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匯編實現的解壓代碼: [html] /* 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字節空間。