最近はシミュレータを利用して開発を進めているのだけど(だって楽だからね), ちょっと気になることがある.というのは,シミュレータがsleep命令に対応して いないため,idleタスクがビジーループで回りっぱなしになっているので, 何もしてないときでもPCの負荷がやたら高くなっていることだ.
ということでgdbのH8シミュレータにsleep命令対応を入れようと思う. 方法だが,sleep命令が実行されたときには select() で仮想デバイスからの 割り込み待ちに入ってしまえばいいだろう.
ここで利用しているgdbのH8シミュレータは,ぼくが仮想デバイスの対応を入れて いるものなので,当然ながらオリジナルにはそのような割り込み待ちの処理は無い. なので,割り込み待ち処理を自前で入れる必要がある.
ということで,H8シミュレータの compile.c と device.c に対応を入れてみた. 以下がそのソース.ちなみにブートローダーは前々回 と同じ.
上記ソースコードのgdbというフォルダにあるのが,gdbへのパッチ. パッチの当てかたは今までと同様で, 以前のシミュレータ対応までのパッチ当てした状態に, さらに上記ソースコードの device.c.patch と compile.c.patch をパッチ当て すること.なお device.c は,パッチ当て後のものも上記ソースコードで配布しているので, パッチ当てするのでなくそっちに置き換えるというのでもよい. device.c は今までで何度も修正しているためひとつひとつパッチ当てしていくのは 面倒なので,置き換えてしまったほうが確実だろう.
compile.c は,シミュレータ編第2回のときの 修正に加えて,上で配布している compile.c.patch を当てる,ということになる. つまりこちらは2つのパッチを当てる必要があるということだ.
まず,device.c に対する修正について説明しよう. 以下は device.c.patch の内容.
--- device.c.old5 Thu Oct 21 12:53:28 2010 +++ device.c Mon Nov 15 22:24:53 2010 @@ -474,26 +474,6 @@ return; } -static int ether_is_recv(int fd) -{ - fd_set fds; - struct timeval timeout; - int ret; - - timeout.tv_sec = 0; - timeout.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(fd, &fds); - ret = select(fd + 1, &fds, NULL, NULL, &timeout); - if (ret > 0) { - if (FD_ISSET(fd, &fds)) { - return 1; - } - } - - return 0; -} - #define RTL8019_ADDR 0x200000 #define NE2000_CR (memory[RTL8019_ADDR + 0x00]) #define RTL8019_RDMAP (memory[RTL8019_ADDR + 0x10]) @@ -562,7 +542,7 @@ NE2000_ISR = 0; /* 受信した */ - if (ether_is_recv(ether_fd)) { + if (is_recv_enable(sim_callback, ether_fd)) { size = read(ether_fd, buf, sizeof(buf)); if (size == sizeof(buf)) { fprintf(stderr, "buffer over %d\n", size); @@ -670,6 +650,28 @@ ether_init(sim_callback, memory); initialized++; +} + +int sim_device_sleep(host_callback *sim_callback, unsigned char *memory) +{ + fd_set fds; + int ret, fd; + + FD_ZERO(&fds); + FD_SET(serial_fd, &fds); + FD_SET(ether_fd, &fds); + + fd = serial_fd; + if (fd < ether_fd) fd = ether_fd; + + ret = select(fd + 1, &fds, NULL, NULL, NULL); + if (ret > 0) { + if (FD_ISSET(fd, &fds)) { + return 1; + } + } + + return 0; } int sim_device_run(host_callback *sim_callback, unsigned char *memory)ether_is_recv() は is_recv_enable() と同等なのでひとつにまとめた. さらに select() で各種仮想デバイスの入力待ちをするための関数 sim_device_sleep() を追加した.select() の最後の引数(タイムアウト値)を NULLに指定しているので,入力があるまで待ち合わせることになる.
次に compile.c に対する修正.以下,compile.c.patch の内容.
--- compile.c.old Thu Oct 21 12:48:00 2010 +++ compile.c Mon Nov 15 22:34:18 2010 @@ -45,20 +45,21 @@ static SIM_OPEN_KIND sim_kind; static char *myname; /* FIXME: Needs to live in header file. This header should also include the things in remote-sim.h. One could move this to remote-sim.h but this function isn't needed by gdb. */ static void set_simcache_size (SIM_DESC, int); extern void sim_device_init (host_callback *sim_callback, unsigned char *memory); +extern int sim_device_sleep (host_callback *sim_callback, unsigned char *memory); extern int sim_device_run (host_callback *sim_callback, unsigned char *memory); #define X(op, size) (op * 4 + size) #define SP (h8300hmode && !h8300_normal_mode ? SL : SW) #define h8_opcodes ops #define DEFINE_TABLE #include "opcode/h8300.h" @@ -1910,20 +1911,21 @@ int rd; int ea; int bit; int pc; int c, nz, v, n, u, h, ui, intMaskBit; int trace, intMask; int oldmask; enum sim_stop reason; int sigrc; int vec; + int sleeping = 0; init_pointers (sd); control_c_sim_desc = sd; prev = signal (SIGINT, control_c); if (step) { sim_engine_set_run_state (sd, sim_stopped, SIGTRAP); } @@ -1968,20 +1970,26 @@ #if ADEBUG if (debug) { printf ("%x %d %s\n", pc, code->opcode, code->op ? code->op->name : "**"); } h8_increment_stats (sd, code->opcode); #endif + if (sleeping) + { + sim_device_sleep(sim_callback, h8_get_memory_buf(sd)); + sleeping = 0; + } + vec = sim_device_run (sim_callback, h8_get_memory_buf(sd)); if (vec && !intMaskBit) { tmp = h8_get_reg (sd, SP_REGNUM); if(h8300_normal_mode) { tmp -= 2; SET_MEMORY_W (tmp, code->next_pc); tmp -= 2; SET_MEMORY_W (tmp, h8_get_ccr (sd)); @@ -3675,20 +3683,26 @@ } h8_set_reg (sd, SP_REGNUM, tmp); goto end; case O (O_ILL, SB): /* illegal */ sim_engine_set_run_state (sd, sim_stopped, SIGILL); goto end; case O (O_SLEEP, SN): /* sleep */ +#if 1 + if (1) { + sleeping = 1; + goto next; + } else +#endif /* Check for magic numbers in r1 and r2. */ if ((h8_get_reg (sd, R1_REGNUM) & 0xffff) == LIBC_EXIT_MAGIC1 && (h8_get_reg (sd, R2_REGNUM) & 0xffff) == LIBC_EXIT_MAGIC2 && SIM_WIFEXITED (h8_get_reg (sd, 0))) { /* This trap comes from _exit, not from gdb. */ sim_engine_set_run_state (sd, sim_exited, SIM_WEXITSTATUS (h8_get_reg (sd, 0))); } #if 0sleep命令が実行された際には sim_device_sleep() を呼び出すことで select()による仮想デバイスからの入力待ちに入る.
最後に,OS側の main.c に修正を入れる.
diff -ruN h8_08/os/main.c h8_09/os/main.c --- h8_08/os/main.c Thu Nov 11 22:07:43 2010 +++ h8_09/os/main.c Mon Nov 15 22:35:00 2010 @@ -24,9 +24,7 @@ kz_chpri(15); /* 優先順位を下げて,アイドルスレッドに移行する */ INTR_ENABLE; /* 割込み有効にする */ while (1) { -#ifndef SIMULATOR asm volatile ("sleep"); /* 省電力モードに移行 */ -#endif } return 0;今まではシミュレータ動作の場合には sleep 命令を無効にしてidleタスクを ビジーループにしていたが,今回sleepに対応したので外す.
これでシミュレータ動作の際に,何もしていないときには select() 待ちで スリープ状態に入るため,PCの負荷を格段に低くすることができる.
注意として,上記で配布しているソースコードは,Makefileが実機動作用の 状態になっている.シミュレータで動作させる際には 前回の最後の説明を参考にして,Makefile に手を入れて からファームウェアを作成すること.