Inline Assembly

The general format of the asm (or __asm__) construct:

asm( assembler template [: output operands : input operands : list of clobbered registers])

assembler template
  • Put assembler instructions here.
  • Format: "assembler instruction(s)". Each instructions is followed by a semicolon or a newline (\n).
  • %0 for the first operand, %1 for the second operand,…etc.
output operands or input operands
  • List of operands in C expressions.
  • Format: “constraint” (C expression). Each operand is separated by a comma.
  • The output operands can be written to by instructions. The input operands can be read by the instructions.
  • The gcc website maintains an exhaustive list of machine constraints. Just scroll down a few pages to the Blackfin family section.
list of clobbered registers
  • List of modified registers. This will prevent the compiler from using these registers for other purposes.
  • Format: enclosed in double quotes (” ”). Each register is separated by a comma.

example:

int x =10;
int y;

asm(
"%0 = %1" /* y=x */
:"=r"(y) /* output operand (%0): write-only, register constrained variable y */
:"m"(x) ); /* input operand (%1): memory variable x */

asm volatile

The volatile construct should be used if the code must remain in a certain location or should not otherwise be optimized by the compiler.

參考文獻

  1. Blackfin Linux Docs: Using In-line Assembly, Calling External Assembly from C
  2. For more information on asm construct see: GCC-Inline-Assembly-HOWTO.

delay count

一個簡易的 delay 迴圈

void delay_count(int count)
{
volatile int i; /* not optimized out */

for(i=0;i<count;i++);
}
編譯結果,需要兩個 register R0, R1,及 4-byte
ff6005b4 <_delay_count>: /* size 0x22 */
ff6005b4: 00 e8 01 00 LINK 0x4; /* (4) */
ff6005b8: 08 30 R1 = R0;
ff6005ba: 00 60 R0 = 0x0 (X); /* R0=0x0( 0) */
ff6005bc: f0 bb [FP -0x4] = R0;
ff6005be: f0 b9 R0 = [FP -0x4];
ff6005c0: 01 09 CC = R1 <= R0;
ff6005c2: 07 18 IF CC JUMP 0xff6005d0 <_delay_count+0x1c>;
ff6005c4: f0 b9 R0 = [FP -0x4];
ff6005c6: 08 64 R0 += 0x1; /* ( 1) */
ff6005c8: f0 bb [FP -0x4] = R0;
ff6005ca: f0 b9 R0 = [FP -0x4];
ff6005cc: 88 08 CC = R0 < R1;
ff6005ce: fb 1f IF CC JUMP 0xff6005c4 <_delay_count+0x10> (BP);
ff6005d0: 01 e8 00 00 UNLINK;
ff6005d4: 10 00 RTS;
如果區域變數 i 沒加 volatile,會被編譯器最佳化,而整個省略成如下。在程式 delay_count() 的呼叫也會被省略。
ff6005b4 <_delay_count>:
ff6005b4: 00 00 NOP;
ff6005b6: 00 00 NOP;
ff6005b8: 00 e8 00 00 LINK 0x0; /* (0) */
ff6005bc: 01 e8 00 00 UNLINK;
ff6005c0: 10 00 RTS;

程式可以作一些調整,節省一些資源
  • 採用 down counter 可直接判斷是否為 0,省用 R1
  • 省略一開始是否為 0 的判斷
  • 不存回
void delay_count(volatile int count)
{
do {
--count;
} while(count);
}
編譯結果 (藍色字是可再節省的部份)
ff6005b4 <_delay_count>: /* size: 0x18 */
ff6005b4: 00 e8 00 00 LINK 0x0; /* (0) */
ff6005b8: b8 b0 [FP + 0x8] = R0;
ff6005ba: b8 a0 R0 = [FP + 0x8];
ff6005bc: f8 67 R0 += -0x1; /* ( -1) */
ff6005be: b8 b0 [FP + 0x8] = R0;
ff6005c0: b8 a0 R0 = [FP + 0x8];
ff6005c2: 00 0c CC = R0 == 0x0;
ff6005c4: fb 17 IF !CC JUMP 0xff6005ba <_delay_count+0x6> (BP);
ff6005c6: 01 e8 00 00 UNLINK;
ff6005ca: 10 00 RTS;
改用組語寫:
void delay_count(int count)
{
asm volatile (
"1:"
"%0 += -0x1;"
"CC = %0 == 0x0;"
"IF !CC JUMP 1b;"
:
:"r"(count)
);
}
編譯結果,橘色字的部份是核心,大小 6 bytes:
ff6005b4 <_delay_count>: /* size 0x14 */
ff6005b4: 00 00 NOP;
ff6005b6: 00 00 NOP;
ff6005b8: 00 e8 00 00 LINK 0x0; /* (0) */
ff6005bc: f8 67 R0 += -0x1; /* ( -1) */
ff6005be: 00 0c CC = R0 == 0x0;
ff6005c0: fe 13 IF !CC JUMP 0xff6005bc <_delay_count+0x8>;
ff6005c2: 01 e8 00 00 UNLINK;
ff6005c6: 10 00 RTS;

有沒有可能進一步改進呢?

LZW 壓縮

LZW 是一種 lossless dictionary-based compression,用於 GIF、TIFF、UNIX Compress、PDF 檔等。

  • Dictionary is dynamically constructed in the compression and decompress process, so it does not have to be transmitted.
  • This compression technique is capable of achieving respectable compression ratios, typically on the order of 50 ~ 60%, while consuming about 2K of RAM. In larger RAM memory sizes, 8K or 16K, it is possible to achieve 80% efficiency or more.

參考文獻

  1. Wikipedia: LZW (中)
  2. Lossless Data Compression for Embedded Systems

mmap @ user space

參考:Linux程式設計-13.記憶體對映mmap 或 GNU libc manual 13.7 Memory-mapped I/O
mmap() 將檔案當成記憶體使用,將 file 從 offset 開始、長度 length 的區塊映射到 user virtual memory 供存取。

#include
void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);
int munmap(void *start, size_t length);

  • start:user 想要的位置,通常可不指定
  • prot: protection mode (read/write/exec/none)
  • flags:
    • MAP_PRIVATE: 另外取得自用的記憶體區塊,寫入只寫到自用的記憶體區塊,不會寫回檔案。
    • MAP_SHARED: 使用共用的記憶體區塊得以立即共用包括修改的部份,會寫回檔案,但可能不會立即寫回。
    • MAP_FIXED: mapped exactly at ADDR (must be page-aligned), otherwise a convenient nearby address is used.
    • ...
int msync(const void *start, size_t length, int flags);
立即寫回檔案

參考:Zero Copy I: User-Mode Perspective
在某些情況下,mmap() 可以取代 read() 減少一次將資料複製到 user space 的動作。

參考:mmap Sample Code
實體記憶體也是檔案 /dev/mem,mmap() 可以讓 user space 存取實體記憶體,例如週邊的 registers,如此便可以在 user space 設定週邊。但在 user space 要將資料寫到 kernel space 會花些時間。

延伸閱讀

  1. 在驅動程式,ioremap 將 physical address 映射到 virtual address。
  2. remap_page_range():待研究

讀 Zero Copy I: User-Mode Perspective

  • 使用 read(file, tmp_buf, len) 及 write(socket, tmp_buf, len) 會有四次 user/kernel context switches 及四次資料複製:hard disk -> kernel buffer -> user buffer -> socket buffer -> NIC
  • 使用 tmp_buf = mmap(file, len) 及 write(socket, tmp_buf, len) 可共用 kernel buffer 及 user buffer,可減少一次複製,但需要處理 write() 的時候,file 有變更造成的錯誤
  • 使用 sendfile(socket, file, len) 複製次數一樣,但 user/kernel context switches 變為兩次
  • 如果 NIC 支援 gather,sendfile(socket, file, len) 可再減少一次複製,不用複製到 socket buffer 就能將資料傳給 NIC
  • 有沒有可能直接 hard disk 到 NIC 呢?

具有跨平台同步功能的網路硬碟 Dropbox

OSSF這篇提到可用 Dropbox跨平台同步你的密碼檔案,剛好是我之前一直想找的且符合我需求的網路空間。

Dropbox 提供免費的 2G 網路空間,特別的是它提供跨 Windows、Mac、Linux、甚至 iPhone (介紹) 的同步軟體,只要將檔案放在電腦的 Dropbox 資料夾下,軟體自動會同步到網路空間及其它所有有安裝 Dropbox 的電腦。此外,也可設定子資料夾的分享權限,並且有版本管控功能,可回復到之前的狀態。

使用此連結申請 Dropbox 可多 250 MB 空間。

Linux/Ubuntu 看公視免費 VOD

公視免費 VOD 播放時除了需要支援 Windows Media Player 格式外,也用到 ActiveX 控制項。在 Firefox 安裝擴充套件 MediaWrap,將 ActiveX 多媒體控制項轉換成 Firefox 可支援的形式 (這裏有介紹),雖然無法快轉,或中途看起 (可能在 Windows 下的 IE 也是如此),但至少可以順利播放了。