最新

DreamPlugで省エネサーバー

2011| 06| 07| 08| 09|
2014| 01|
2015| 11|

プロフィール
大阪府島本町在住
本業は分析機器のファームウエア開発

将棋のプログラムMiyako Shogi Systemを作っています

2015/11/07

Watch Dog Timer

ローダブルモジュールが使えればデバイスドライバorion_wdtを使うのがスマートだが、

ハードウエア直たたきでも問題ないだろう。

このページを参考にしてCで作成してみた。

http://plugcomputer.org/plugforum/index.php?topic=466.0

約21秒でtime upするWatch Dog Timerである。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/io.h>
#include <sys/mman.h>

typedef uint64_t u64;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t  u8;
typedef int64_t  s64;
typedef int32_t  s32;
typedef int16_t  s16;
typedef int8_t   s8;

main()
{
#define REG_SIZE    0xfff
#define PAGE_SIZE   (sysconf(_SC_PAGE_SIZE))
int  mem_fd;
u8   *preg;

    mem_fd=open("/dev/mem",O_RDWR|O_CREAT,0666);
    if ( mem_fd < 0 ) {
        puts("/dev/mem error.");
        return 1;
    }

    preg=(u8 *)malloc(REG_SIZE+(PAGE_SIZE-1));      // PAGE_SIZEでアラインメント
    if ( (u32)preg % PAGE_SIZE ) {
        preg+=(PAGE_SIZE-((u32)preg % PAGE_SIZE));
    }

    preg=(u8 *)mmap((caddr_t)preg,REG_SIZE,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_FIXED,
mem_fd,0xf1020000ul);
    *(u32 *)(preg+0x0324)=0xfffffffful;     // カウンタ値(約21秒)
    *(u32 *)(preg+0x0300)|=0x10;            // WDT enable
    *(u32 *)(preg+0x0108)=0x2;              // WDT start

    for (;;) {
        *(u32 *)(preg+0x0324)=0xfffffffful;     // カウンタ値リロード
        puts("WDT clear.");
        sleep(15);                              // 15秒毎にWDTクリア
    }
}

これを gcc -O2 -o wdt_dp wdt_dp.c でコンパイルして、

./wdt_dp & と動かせばよい。(root必須)

2014/01/04

u-bootを書き換える

https://code.google.com/p/dreamplug/downloads/list

にある、

u-boot-DreamPlug.kwb

がu-boot F/Wである。これをダウンロードし、SDカードの第1パーティション(カーネルのあるパーティション)にu-boot.kwbと名前を変えてコピーする。

シリアルケーブをつなぎu-boot設定画面に入る。

設定が初期化されるので、printenv で設定を控えておく。

usb start

fatload usb 1:1 0x0800000 u-boot.kwb

sf probe 0

sf erase 0x0 0x60000

sf write 0x0800000 0x0 0x60000

reset

SDカードからの起動を早くする

net bootを試みるためにサーバーにアクセスするためタイムアウトの分だけ起動が遅くなる。

サーバーにアクセスしないように x_bootcmd_ethernet の設定を空にする。

setenv x_bootcmd_ethernet

saveenv

reset

2011/09/26

筐体温度を測る

そもそもこのCPUの発熱は小さいが、サーバー用途ならシステム温度のトレンドを見たいので、コンソール用のシリアルポート経由でマイコンを用いて筐体の温度を測ることにしよう。

マイコンには今までPICマイコンを愛用していたが、今回はルネサスR8C/M12Aを使った(R5F2M122ANDD)。秋月で100円で売っているこれはプログラムメモリ2KB品なので注意。温度測定は抵抗とサーミスタの分圧をマイコンのA/Dで読み、近似式で温度変換する。サーミスタは石塚電子製103AT。回路図

プログラム開発はHEW+R8C/M16C C/C++コンパイラ+E8aデバッガ

E8aでデバッグ中。

稼動中の様子。サーミスタは本体にキッチンアルミテープで貼り付けている。

R8Cのソースコード

DreamPlugよりシリアルポートから文字列「#t_e_m_p.」をマイコンへ送信すると、マイコンからセ氏温度を10倍した値を返す。A/D値→温度変換は3次の近似式。驚くかな、floatが使える。コードサイズぎりぎり。

#include    <string.h>
#include    "sfr_r8m12a.h"

// *** 定義
typedef unsigned char   BYTE;
typedef unsigned short  WORD;
typedef unsigned short  DWORD;
typedef unsigned char   BOOL;

#define BUFFERSIZE  8   // UART受信バッファ数

WORD    timer1;     // 100Hzタイマーカウンター1
WORD    timer2;     // 100Hzタイマーカウンター2

const char  *magic="#t_e_m_p."; // 合致文字列
BYTE    magic_len;              // 合致文字列長

char    rx_buff[BUFFERSIZE];    // UART受信バッファ
BYTE    rx_len;     // UART受信バッファ長

WORD    ntc(WORD x);        // NTCサーミスタによる温度変換
void    bin2dec(char *s, WORD x);   // bin → dec変換
WORD    adc(void);      // A/D変換
char    getc(void);     // 受信バッファから1文字取り出し
void    putc(char c);   // 1文字送信
void    puts(char *s);  // 1行送信

void main(void)
{
WORD    ad;     // A/D値

   // *** ポート初期化
   pd1_7=1;            // P1_7 出力
   p14sel0=1;          // P1_4 TXD
   p14sel1=0;
   p15sel0=1;          // P1_5 RXD
   p15sel1=0;

   // *** クロック設定
   prc0=1;             // プロテクトOFF

   locodis=0;          // 低速オンチップオシレータ発振開始
   hocoe=1;            // 高速オンチップオシレータ発振開始

   asm("nop");         // 発信待ち(適当)
   asm("nop");
   asm("nop");
   asm("nop");
   asm("nop");

   hscksel=1;          // オンチップオシレータ選択
   phisel=0x00;        // 分周なし
   frv1=fr18s0;        // φ=18.432MHz補正レジスタ
   frv2=fr18s1;
   scksel=1;           // 高速クロック選択

   prc0=0;             // プロテクトON

   // *** パワーコントロール
   msttrb=0;           // RBアクティブ (0でアクティブとは...)
   mstuart=0;          // UARTアクティブ
   mstad=0;            // A/Dアクティブ

   // *** RB2タイマーの設定
   tstop_trbcr=1;      // RB2強制停止
   tstop_trbcr=0;      // RB2動作

   tstart_trbcr=0;     // タイマー停止

   trbmr=0b01010000;   // count source supply, count source = f32, timer mode

   trbpr=36-1;         // 18.432MHz/32/36/160=100Hz
   trbpre=160-1;
   trbir=0b10000000;   // RB2割り込み許可

   ilvlc0=1;           // 割り込みレベル1
   ilvlc1=0;

   tstart_trbcr=1;     // タイマースタート

   // *** A/D設定
   admod=0;            // A/D clk=f8 単発モード
   adinsel=0;          // AN0/グループ0

   // *** UART設定
   u0mr=0b00000101;    // 8n1
   u0c0=0b00010000;    // LSBファースト,CMOS出力,デジタルフィルタON,カウントソース=f1
   u0brg=9;            // 115200bps
   u0rrm_u0c1=0;       // 連続受信モード禁止
   u0tie=0;            // 送信割り込み禁止
   u0rie=1;            // 受信割り込み許可

   ilvl90=0;           // 割り込みレベル2
   ilvl91=1;

   te_u0c1=1;          // 送信許可
   re_u0c1=1;          // 受信許可

   // *** 割り込み許可→メインループ
   asm("LDIPL #0");    // 割り込み優先レベル0(この値よりも高いレベルの割り込みが受け付けられる)
   asm("nop");
   asm("FSET I");      // 割り込み開始

   magic_len=strlen(magic);    // 合致文字列長
   ad=adc();                   // A/D値を求める
   timer1=timer2=0;

   for (;;) {
   char    c, s[16];

       if ( timer1 >= 2 ) {    // A/D開始
           ad=(adc()+ad+1)/2;  // 移動平均

           timer1=0;
       }

       c=getc();
       if ( c ) {      // 受信文字あり
       int n;

           for ( n=0; n < magic_len-1; n++ ) s[n]=s[n+1];  // 1文字シフト
           s[magic_len-1]=c;
           s[magic_len]=0;

           if ( !strcmp(magic,s) ) {   // 合致文字列?
               bin2dec(s,ntc(ad));     // A/D値出力
               puts(s);

               bzero(s,sizeof(s)); // 文字列消去
           }

           timer2=0;
       }

       if ( timer2 >= 50 ) {       // 0.5秒経過で文字列消去
           bzero(s,sizeof(s));

           timer2=0;
           p1_7=1;     // LED消灯
       }
   }
}

WORD ntc(WORD x)  // NTCサーミスタによる温度変換 戻り値  ℃*10
{
float   a=x;

// y = -4.614E-07x3 + 6.409E-04x2 - 3.860E-01x + 9.769E+01
   return ((-4.614e-6*a+6.409e-3)*a-3.860)*a+9.769e+2;    // 10倍にする
}
 
void bin2dec(char *s, WORD x)  // bin → dec変換
{
   s[0]=(x/100)%10+'0';
   s[1]=(x/10)%10+'0';
   s[2]=x%10+'0';
   s[3]=0;
}

WORD adc()  // A/D変換
{
   adst=1;             // 変換開始
   while ( !adf );     // 変換待ち

   return ad0;
}

char getc()  // 受信バッファから1文字取り出し 戻り値  文字があれば文字, 文字がなければ 0
{
static BYTE ptr;    // バッファポインタ

   if ( rx_len ) {     // 受信データあり
   BYTE    c;

       c=rx_buff[ptr&(BUFFERSIZE-1)];
       ptr++;
       rx_len--;

       return c;
   }
   else return 0;
}

void putc(char c)  // 1文字送信
{
   while ( !ti_u0c1 );     // 送信バッファ空?

   u0tbh=0;
   u0tbl=c;
}

void puts(char *s)  // 1行送信
{
   while ( *s ) {
       putc(*s++);
   }
   putc('\r');
   putc('\n');
}

#pragma interrupt   _uart0_receive(vect=18)
void _uart0_receive(void)  // UART受信割り込み
{
static BYTE ptr;    // バッファポインタ
BYTE    t;
WORD    tmp;

   t=u0rif;
   u0rif=0;        // 割り込みクリア

   tmp=u0rb;
   if ( tmp & 0xf000 ) {   // 受信エラー復帰
       te_u0c1=0;          // 送信停止
       re_u0c1=0;          // 受信停止
       u0mr=0;             // SIO無効
       u0mr=0b00000101;    // 8n1
       te_u0c1=1;          // 送信許可
       re_u0c1=1;          // 受信許可
   }
   else {      // 受信OK
       if ( rx_len < 8 ) {
           rx_buff[ptr&(BUFFERSIZE-1)]=tmp & 0xff;
           ptr++;
           rx_len++;
       }

       p1_7=0;     // LED点灯
   }
}
 
#pragma INTERRUPT   _timer_rb2(vect=24)
void _timer_rb2(void)  // RB2割り込み
{
BYTE    t;

   t=trbif_trbir;
   trbif_trbir=0;      // 割り込みクリア

   timer1++;
   timer2++;
}

DreamPlug側のプログラム(perl版)

/etc/inittabのT0:23:respawn:/sbin/getty 115200 ttyS0の行をコメントアウトし、libdevice-serialport-perlをインストールして、perlでシリアルポートを使えるようにする。

#!/usr/bin/perl
use Device::SerialPort;

# 温度読み込み
my $port=new Device::SerialPort('/dev/ttyS0') || die;
$port->user_msg(1);
$port->user_msg(1);
$port->error_msg(1);
$port->baudrate(115200);
$port->databits(8);
$port->parity("none");
$port->stopbits(1);
$port->handshake("none");
$port->read_const_time(50);
$port->read_char_time(0);

my $magic='#t_e_m_p.';  # 合致文字列

$port->write($magic);
my ($len,$sys)=$port->read(8);
$port->close;
($len >= 4) || die;      # 数字3文字+\n
$sys =~ /(\d{3})/;
$sys=$1/10;
print "sys=$sys\n";


2011/09/09

ファイルシステムをext2にする

SDの書き込み速度は3MB/sぐらいしかないのと、書き込み回数を減らしたいため、ファイルシステムをext2にする。

ジャーナリング無しのため最悪ディスク破壊の可能性があるが、確率は低いと考えてよしとする(一応UPSがあるので、瞬停は防げる。定期的なバックアップの励行)。

2011/08/17

シリアル変換基板の製作

シリアル接続ぐらいはGuruPlug JTAGが無くても良いだろうと思っていたので、UARTの口にオシロをつないで信号を見る(ファーム屋の意地)。シリアルレベル変換の簡単な回路でPCと接続できた。部品はマルツパーツで手に入れた。回路図

難儀だったのは、Molex Picobladeの圧着で、工具はこれで、コンタクトは20本も買っていたが、圧着のコツをつかんだのは残り5本となってからだ。PCとはRS-232Cクロスケーブルで接続する。

2011/08/12

HDDをSDカードに変更

さらに省エネのため、HDDをSDカードにする。

SuperTalent SDHCカード 32GB class 10 SDHC32-C10が\3,500ほどで手に入った。

高級SSDでは無いので、プチフリーズはやむを得ないが、圧倒的な低消費電力サーバーとなったので、良しとしよう。

2011/07/31

速度の比較

VIA C3 1GHzのBogoMIPS

Calibrating delay using timer specific routine.. 1997.91 BogoMIPS (lpj=3995831)

DreamPlugのBogoMIPS

Calibrating delay loop... 1191.11 BogoMIPS (lpj=5955584)

VIA C3が同クロックのMMX Pentiumの7割がたの体感速度だったが、ARMは半分ぐらいの感覚か。

2011/07/30

サーバー移設実行日

元のサーバーのWANポートを抜き、外部からのアクセスを切断、rsyncで元のサーバーからデータを移設。

最初、pppが接続せず難儀したが、/etc/ppp/chap-secretsが変なファイルにリンクされていたためである。注意深く元のサーバーから設定ファイルをコピーしたのだが、リンクされたファイルは上書きされないので要注意。



リンクはご自由に

Lighttpd

DreamPlug