コグノスケ


2021年 9月 4日

Zephyr on HiFive Unmatched/HiFive Unleashed

目次: Zephyr を調べる - まとめリンク

Zephyr の HiFive Unmatched と HiFive Unleashed へのポーティングが本家の main にマージ(※)されました。2.7.0 のマージウインドウが終わるまで、メンテナーチームは忙しそうだったので、もうしばらく放置かな?と思っていましたが、昨日突然マージされました。

これは以前トライしていた Zephyr の HiFive Unleashed への移植(2021年 6月 6日の日記参照)に加え、HiFive Unmatched への移植(2021年 8月 20日の日記参照)も追加したものです。RISC-V はメンテナーが少ないこともあって割と放置気味になりがちなんですよね……。

ポーティングといっても、最低限の設定+とりあえず起動するだけで、ほぼ何にも使えない(UART と SPI くらいしか動きません)から、まだまだこれからだな。うん。

(※)最近は master という単語はダメだねってことで、既存のリポジトリも master → main に置き換えられたプロジェクトが増えてます。

メモ: 技術系の話は Facebook から転記しておくことにした。

編集者: すずき(更新: 2021年 10月 6日 01:45)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 9月 5日

ゲーム用の PC を作りたいが、高い

普段 PC で何か作業する場合 Windows のノート PC を使っていて、マイコンボードでの実験や開発は Linux のデスクトップ PC にリモートアクセスして行っています。通常の作業には申し分ない性能と使い勝手が実現できています。

が、ゲームとなると話がやや違ってきます。ゲームは大抵 Windows を要求してくるので、我が家の唯一の Windows PC であるノート PC で遊ぶしかないんですけども、最近の 3D を多用したゲームは重すぎて、そのうち GPU が燃える(物理的に)んじゃないかと心配になってきます。

GPU が高すぎる

しばらく前に在宅勤務環境を整えた(2021年 2月 12日の日記参照)こともあり、机近辺に新たな PC を置けそうな場所ができました。Windows をインストールしたゲーム用 PC を新たに作りたいところですが、最近はあまりにも GPU が高すぎて購入に踏み切れません……。

いわゆるミドルエンドと呼ばれる TDP 200W 以下(※)クラスは、今だと Radeon RX 6600 XT もしくは GeForce RTX 3060 辺りだと思うんですが、Radeon はそもそも売り切れていて手に入りませんし、RTX 3060 はお値段が 6万円オーバーとあまりにも高すぎます。

GPU の値段はまだまだ下がらないようですし、諦めて買うしかないんですかねえ?

(※)補助電源 8pin x 1 くらいのカードをイメージしてます。GPU に供給可能な電力は PCIe カードエッジ(75W)+ 補助電源 8pin x 1(150W)= Max 225W です。

編集者: すずき(更新: 2021年 9月 6日 21:42)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 9月 10日

C 言語の標準的な標準ではない機能

C 言語の math.h ヘッダには、円周率πを表すマクロ M_PI が定義されています。しかしこのマクロ、コンパイラに -std=c99 や c11 を指定すると使えなくなるんですね。C 言語通には常識かもしれませんが、個人的にハマったのでメモしておきます。

M_PI を使ったプログラム

#include <math.h>

int main(void)
{
	return M_PI;
}
C99 としてコンパイルすると M_PI は宣言されていないと言われる
$ gcc -Wall -std=c99 a.c

a.c: In function ‘main’:
a.c:5:9: error: ‘M_PI’ undeclared (first use in this function)
    5 |  return M_PI;
      |         ^~~~
a.c:5:9: note: each undeclared identifier is reported only once for each function it appears in

もし c99 や c11 でも M_PI を使いたい場合は、math.h をインクルードする前に _DEFAULT_SOURCE(_GNU_SOURCE でも良いです)を define すると使えるようになります。

M_PI を使ったプログラム、C99 でも動く版

#define _DEFAULT_SOURCE

#include <math.h>

int main(void)
{
	return M_PI;
}

使えるようになりました。

編集者: すずき(更新: 2021年 9月 15日 00:04)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 9月 13日

Zephyr のコンテキストスイッチ

目次: Zephyr を調べる - まとめリンク

RISC-V 向け Zephyr の新しいコンテキストスイッチ(CONFIG_USE_SWITCH=y)を実装しているのですが、浮動小数点演算つまり FPU を使うスレッドを生成するとハングします。調べてみると、私が実装している場所の外にある、新しいコンテキストスイッチ(zephyr/kernel/include/kswap.h の do_swap() 関数)の実装が今まで(CONFIG_USE_SWITCH=n)と違うように見えます。まだ確証はないですけど。

従来の処理 arch_swap() では、現在のスレッド(_kernel.current)がコンテキストスイッチ(arch_swap() 内のシステムコール)の内部で旧 → 新に置き換えます。つまり current は切替「前」のスレッドを指している状態でコンテキストスイッチが始まります。

ところが do_swap() の場合、現在のスレッド(_kernel.current)がコンテキストスイッチ(arch_switch() 関数)を呼ぶ「前」に旧 → 新スレッドに置き換えます。つまり current は切替「後」のスレッドを指している状態でコンテキストスイッチが始まります。

ハングに至るメカニズム

RISC-V Zephyr(他のアーキテクチャも同じかな?)では FPU 使えるスレッドと使えないスレッドを使い分けることができます。コンテキストスイッチ処理では、current スレッドが FPU を使うか使わないかにより処理を変えています。

  • FPU を使う: current の浮動小数点レジスタをスタックに退避する処理を実行
  • FPU を使わない: 退避する処理はスキップされる

私が実装したコンテキストスイッチも当然同じように実装したのですが……。先ほど説明したように do_swap() は current を切替「後」のスレッドに設定するため、こんな悲劇が起きます。

  • 旧スレッド(FPU 使用不可)→ 新スレッド(FPU 使用可)の明示的コンテキストスイッチを行う
  • do_swap() が current を新スレッド(FPU 使用可)に変える
  • コンテキストスイッチ
  • 旧スレッド(FPU 使用不可)で実行しているのに、current は新スレッド(FPU 使用可)になっている
  • 浮動小数点レジスタを退避しようとして「不正命令例外」で死ぬ

原因の一端は掴めたものの、どうして他のアーキテクチャは困っていないのか?do_swap() の実装は意図的なのか?良くわかりません……。

編集者: すずき(更新: 2021年 9月 21日 22:56)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 9月 16日

ワクチン 2回目

前回(2021年 8月 26日の日記参照)同様に自治体の接種会場に行きました。ワクチンは当然同じでファイザー製です。

医療従事者のみなさま

前回同様に看護師を始めとした医療従事者の皆様は非常に親切かつ効率的に働いていました。ありがてぇ。

問診の先生はやっぱりお疲れモードな雰囲気でした。無理はしないでください……。

肩が痛い

前回はワクチンを打ってしばらく経ってから肩が痛くなりましたが、今回は打った直後から肩が痛いです。明日はどうなるんだろうか、これ……。

編集者: すずき(更新: 2021年 9月 17日 01:59)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 9月 20日

RISC-V の cmodel

目次: RISC-V - まとめリンク

RISC-V には code model という概念があり、ざっくり言うとメモリアクセスやジャンプの際に参照するアドレスの作り方を指定します。medlow と medany の 2つがあり medlow がデフォルトです。

詳しくは GCC のマニュアル RISC-V Options (Using the GNU Compiler Collection (GCC)) や SiFive のエンジニアによる解説 All Aboard, Part 4: The RISC-V Code Models - SiFive を読んでいただくのが良いかと思いますが、ここではどんなときにエラーになるかに重点を置いて、いくつか例を挙げたいと思います。

medlow

モデル medlow は 32bit 絶対値でアドレスを指定します。具体的には lui 命令とロード命令などの 12ビットオフセットを使います。lui 命令とは 20ビットの immediate を 12ビット左シフトして、符号拡張する命令のことです。

medlow 指定時に生成されるコードの例
lui a1, 0x12345    # 12ビットシフトされた値 0x12345000 が a1 に格納される
ld a0, 0x678(a1)   # アドレス a1 + 0x678 = 0x12345000 + 0x678 から a0 に値をロードする

このようなコードが生成されます。絶対値は -2GB 〜 +2GB までしか生成できませんので、全てのシンボルが範囲に収まっている必要があります。32bit アドレスを使っている場合は全てのアドレス範囲をカバーできますが、64bit アドレスを使っている場合は 0x00000000_00000000〜0x00000000_7fffffff または 0xffffffff_80000000〜0xffffffff_ffffffff のアドレス範囲にシンボルを配置しなければなりません。範囲外にシンボルを配置しようとすると、

medlow のアドレス生成範囲外となるケース

// a.c

extern volatile int *hoge;

void _start(void)
{
	*hoge = 1;
}


/* a.ld */

OUTPUT_ARCH("riscv")
ENTRY(_start)

SECTIONS
{
	PROVIDE(hoge = 0x100000000);    /* 0x1_00000000 は medlow の範囲外 */
}
medlow のアドレス生成範囲外となるケース(リンク結果)
$ riscv64-zephyr-elf-gcc -march=rv64gc -Wall -g -mabi=lp64d -mcmodel=medlow -nostdlib -T a.ld a.c --save-temp

a.o: in function `_start':
test-medany/a.c:5:(.text+0x6): relocation truncated to fit: R_RISCV_HI20 against symbol `hoge' defined in *ABS* section in a.out
collect2: error: ld returned 1 exit status

リンカーがエラーを出します。hoge のアドレスを変更し -2GB 〜 +2GB の範囲(0xffffffff_80000000 や 0x00000000_70000000 など)にするとリンクが通ります。

medany

もう 1つの medany は、PC 相対でアドレス指定します。具体的にはアドレスの場合は auipc 命令と addi 命令、ジャンプの場合は auipc 命令と jalr 命令の 12ビットオフセットを使います。auipc 命令とは、20ビットの immediate を 12ビット左シフトして、符号拡張したあと PC に加算する命令のことです。

PC が 0x1_40000000 付近で hoge が 0x1_a89abcd0 だとすると、

medany 指定時に生成されるコードの例
アドレスの場合

140000006: auipc   a5,0x689ac    # 12ビットシフトされた値 0x689ac000 + PC 0x1_40000006 = 0x1_a89ac006 が a5 に格納される
14000000a: addi    a5,a5,-822    # 0x1_a89ac006 - 822 = 0x1_a89abcd0 = hoge のアドレスが a5 に格納される


ジャンプの場合

140000008: auipc   ra,0x689ac    # 12ビットシフトされた値 0x689ac000 + PC 0x1_40000008 = 0x1_a89ac008 が ra に格納される
14000000c: jalr    -824(ra)      # ra 0x1_a89ac006 - 824 = 0x1_a89abcd0 = hoge のアドレスにジャンプする

このようなコードが生成されます。相対アドレスは PC の現在地 -2GB 〜 +2GB までしか生成できません。medlow モデルより対応できる範囲は広がったものの、いかなるアドレスでも対応できるわけではないです。例えばコード領域とデータ領域をあまりにも遠くすると、

medany のアドレス生成範囲外となるケース

// a.c

extern volatile int *hoge;

void _start(void)
{
	*hoge = 1;
}


/* a.ld */

OUTPUT_ARCH("riscv")
ENTRY(_start)

MEMORY
{
	TEXT(rx)  : ORIGIN = 0x0000000140000000, LENGTH = 0x10000
}

SECTIONS
{
	/* hoge をコード領域から 2GB 以上離して配置する */
	PROVIDE(hoge = 0x1c89abcd0);

	/* コード領域を 0x1_40000000 にする */
	.text : {
		*(.text*);
	} > TEXT
}
medany のアドレス生成範囲外となるケース(リンクの結果)
$ riscv64-zephyr-elf-gcc -march=rv64gc -Wall -g -mabi=lp64d -mcmodel=medany -nostdlib -T a.ld a.c --save-temp

a.o: in function `_start':
test-medany/a.c:5:(.text+0x6): relocation truncated to fit: R_RISCV_PCREL_HI20 against symbol `hoge' defined in *ABS* section in a.out
collect2: error: ld returned 1 exit status

シンボル hoge が位置するアドレスは絶対値 -2GB 〜 +2GB の範囲外であり、コード領域からも離れているため PC 相対 -2GB 〜 +2GB の範囲外でもあります。よって medlow モデルでも medany モデルでもリンクエラーとなります。

編集者: すずき(更新: 2021年 9月 21日 23:04)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 9月 23日

耳年齢チェック

何 kHz の音まで聞こえるかテストするサイト、聞こえチェック | Panasonic が、以前 Twitter でちょっと話題になりました。

私の場合 15kHz までは聞こえますが、それ以上(17kHz, 19kHz)は全く聞こえません。鳴ってんのか?これ??

測ってみよう

まずブラウザの影響を排除するため、上記のサイトから音源をダウンロードします。Wav ではなく MP3 ファイルでした。

直接オーディオプレイヤーで聞いても 15kHz 以外は聞こえません。ブラウザのせいじゃなかった。私の耳は全くあてにならないので、オシロスコープにご登場願います。


15kHz 再生時の波形


17kHz 再生時の波形


19kHz 再生時の波形(グラフはキャプチャし忘れて 17kHz のまま。右下の周波数表示が 19kHz を示している)

いやあ、バッチリ綺麗に Sin 波が鳴ってます。私は全く聞こえませんね、これが老いかぁ……。

編集者: すずき(更新: 2021年 9月 29日 00:21)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 9月 26日

xterm の 256色端末

まれに xterm の 256 色指定エスケープシーケンスに対応していない端末があって vim の表示が変な色になってしまいます。チェック用のスクリプトを作っておきました。単純に背景色を変更するエスケープシーケンスと、空白文字、色を元に戻すエスケープシーケンスを連打するだけです。

xterm 256色指定エスケープシーケンステスト用スクリプト

#!/bin/sh

ESC_ORG="\e[0m"

print_colors()
{
	for i in ${*};
	do
		printf " %3d\e[%dm  " ${i} ${i};
		echo -n ${ESC_ORG}
	done
	echo
}

print_xterm_colors()
{
	for i in ${*};
	do
		printf " %3d\e[48;5;%dm  " ${i} ${i};
		echo -n ${ESC_ORG}
	done
	echo
}

echo "System colors (ESC[Nm):"
print_colors `seq 40 47`

echo
echo "xterm 256 colors (ESC[48;5;Nm):"
for i in `seq 0 8 248`;
do
	j=`expr ${i} + 7`
	print_xterm_colors `seq ${i} ${j}`
done

実行するとこんな感じになります。


スクリプトの実行結果

対応していない端末だとこうなりますと言いたいところでしたが、対応していない端末が見当たりませんでした。前はあった気がするんだけどなあ……?

編集者: すずき(更新: 2021年 9月 29日 01:32)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 9月 28日

マルチコアのブート処理

メイン CPU からサブ CPU を起こすとき基本的には、

  • CPU の ID(RISC-V であれば CSR の mhartid)を見て、自身がメインかサブかを知る
  • サブ側は共有 RAM をポーリングなどで見張り、メイン CPU からの起動司令を受け取るまで待つ
  • メイン側は共有 RAM に値を書いてサブ CPU に起動司令を送る

RAM の初期値が不定であると仮定すると、サブ CPU が下手にポーリングすると、不定値によって条件が成立してしまい、メイン CPU からの起動司令がないのに勝手に起動してしまう事態に陥ります。

素朴な実装

先程書いた基本的な構造を素直に書くとこんなコードになるでしょう。

素朴なマルチコアのブート

/* メイン CPU は HARTID=8, サブ CPU は HARTID=0...3 とする */

#define HARTID_MAIN         8
#define HARTID_SUB_START    0
#define HARTID_SUB_END      3

#define HARTID_MAX          9

struct {
	int boot_wait;
	int boot_done;
} init_core[HARTID_MAX] = {};

int get_hartid(void)
{
	int i;
	__asm__ volatile("csrr %0, mhartid" : "=r"(i));
	return i;
}

/* メイン CPU が実行する */
void boot_main(void)
{
	for (int i = HARTID_SUB_START; i < HARTID_SUB_END; i++) {
		init_core[i].boot_wait = 1;
	}
}

/* サブ CPU が実行する */
void boot_sub(void)
{
	int hartid = get_hartid();

	while (!init_core[hartid].boot_wait) {
		/* busy loop */
	}
}

残念ながらこのコードは正常に動作しません。共有 RAM つまり init_core[hartid].boot_wait の値が起動直後から != 0 だったとき、boot_sub() は boot_main() からの起動司令を待つことなく起動してしまうからです。

不定値への対処

共有 RAM の不定値に対処する方法を考えます。基本的にはサブ CPU が変数を初期化(boot_wait = 0)してから待ちに入れば良いのですが、新たな問題が生じます。メイン CPU とサブ CPU の実行順序はどちらが先という保証はないため、

  • メイン CPU からの起動司令 boot_wait = 1
  • サブ CPU で変数を初期化 boot_wait = 0

以上の順で実行されるとメイン CPU 側の起動司令が消されてしまい、ハングアップする可能性があります。この問題の回避のため、変数を 1つ追加し、サブ CPU のブートが終わるまで、メイン CPU は繰り返し起動司令を送るように変更します。

  • 変数を 2つ用意する(boot_wait, boot_done)
  • メイン: boot_done = 0
  • メイン: サブから応答(boot_done != 0)があるまで、boot_wait = 1 を書き続ける
  • サブ: boot_wait = 0
  • サブ: boot_done = 0
  • サブ: boot_wait == 0 なら待つ
  • サブ: boot_done = 1

先程書いた基本的な構造を素直に書くとこんなコードになるでしょう。

不定値への対処を入れたコード

/* メイン CPU は HARTID=8, サブ CPU は HARTID=0...3 とする */

#define HARTID_MAIN         8
#define HARTID_SUB_START    0
#define HARTID_SUB_END      3

#define HARTID_MAX          9

struct {
	int boot_wait;
	int boot_done;
} init_core[HARTID_MAX] = {};

int get_hartid(void)
{
	int i;
	__asm__ volatile("csrr %0, mhartid" : "=r"(i));
	return i;
}

/* メイン CPU が実行する */
void boot_main(void)
{
	for (int i = HARTID_SUB_START; i < HARTID_SUB_END; i++) {
		init_core[i].boot_done = 0;
		while (!init_core[i].boot_done) {
			init_core[i].boot_wait = 1;
		}
	}
}

/* サブ CPU が実行する */
void boot_sub(void)
{
	int hartid = get_hartid();

	init_core[hartid].boot_wait = 0;
	init_core[hartid].boot_done = 0;

	while (!init_core[hartid].boot_wait) {
		/* busy loop */
	}
	init_core[hartid].boot_done = 1;
}

残念ながらこのコードも正常に動作しません。共有 RAM への値の反映が他の CPU に即座に見えること(アトミック性)を暗に期待しているからです。

アトミック性への対処

今日のマルチコアシステムでは、boot_wait = 0 としたときに、他の CPU にも即座に同じ値が見えているとは限りません。主な要因としては、

  • コンパイラによる並べ替え
  • CPU のパイプライン
  • CPU のデータキャッシュ、ライトバッファ

などがあります。通常の変数への代入、参照が他の CPU に即座に値が見えないことにより、おかしくなるパターンはいくつか考えられそうですが、ありがちなパターンとして、

  • サブ CPU 0 が起動した boot_done = 1
  • メイン CPU に boot_done = 1 が伝わらず、サブ CPU 1 の起動指令がいつまでも送られない

以上の順で実行されるとメイン CPU 側が起動司令を送らないまま、サブ CPU 側も何もできずハングアップする可能性があります。この問題の回避のため、通常の変数への代入、参照ではなく他の CPU にも値が見えるように初期化、代入(アトミックアクセスする)必要があります。

従来 C 言語でアトミックアクセスを行うためには、実装対象アーキテクチャの知識やアセンブラの記述を必要とするなど、やや困難が伴いました。ですが C11 でアトミックアクセス用の定義 stdatomic.h が追加されたことで、アトミックアクセスはかなり楽になりました。素敵ですね。

ひとまず速度を全く気にせず、全てのアクセスをアトミックアクセスに入れ替えると、こんなコードになるでしょう。

アトミック性への対処を入れたコード

/* メイン CPU は HARTID=8, サブ CPU は HARTID=0...3 とする */

#define HARTID_MAIN         8
#define HARTID_SUB_START    0
#define HARTID_SUB_END      3

#define HARTID_MAX          9

struct {
	atomic_int boot_wait;
	atomic_int boot_done;
} init_core[HARTID_MAX] = {};

int get_hartid(void)
{
	int i;
	__asm__ volatile("csrr %0, mhartid" : "=r"(i));
	return i;
}

/* メイン CPU が実行する */
void boot_main(void)
{
	for (int i = HARTID_SUB_START; i < HARTID_SUB_END; i++) {
		atomic_store(&init_core[i].boot_done, 0);
		while (!atomic_load(&init_core[i].boot_done)) {
			atomic_store(&init_core[i].boot_wait, 1);
		}
	}
}

/* サブ CPU が実行する */
void boot_sub(void)
{
	int hartid = get_hartid();

	atomic_store(&init_core[hartid].boot_wait, 0);
	atomic_store(&init_core[hartid].boot_done, 0);

	while (!atomic_load(&init_core[hartid].boot_wait)) {
		/* busy loop */
	}
	atomic_store(&init_core[hartid].boot_done, 1);
}

C11 のアトミックアクセスは何も指定しない場合、一番制限の強い(= 確実に他の CPU に見えるものの、アクセス速度は遅い)memory_order_seq_cst アクセスになります。マルチコアのブートを行うにあたって、常に制限が強いアクセスは必要ありませんが、とりあえずこれで動くはず。

編集者: すずき(更新: 2021年 9月 30日 11:36)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 9月 30日

携帯の遍歴

日記を漁って携帯の遍歴を書き出してみました。日記を書く習慣がなかった頃の機種や時期は不明です。

J-PHONE, Vodafone
  • 不明(2000年くらい?): SHARP J-SH04 辺り?
  • 不明(2002年くらい?): TOSHIBA J-T06 辺り?(ストレートだった気がする)
NTT ドコモ、ガラケー
  • 2004年 8月: Fujitsu F506i
  • 2005年 6月: Fujitsu F506i マイク故障で交換
  • 2006年 ?月: NEC N902iS
  • 2008年 ?月: NEC N902iS のバッテリー交換
  • 2010年 7月: Panasonic P-03B
NTT ドコモ、スマホ
  • 2011年 7月: Sony Ericsson Xperia acro SO-02C
  • 2014年 3月: SHARP AQUOS PHONE ZETA SH-01F
SIM フリー、スマホ
  • 2016年 11月: ASUS Zenfone 3 Deluxe
  • 2021年 4月: Google Pixel 4a

(基本的には)長く使っていた機種は気に入っていた機種です。ガラケー時代はいずれも良い機種で、バッテリーが死ぬまで使ってました。最後の P-03B だけ 1年しか使っていませんが、不満があったわけではなく、知人に携帯を譲るため手放しました。たしか。

スマホ時代は国内メーカーの質は明らかに落ちました。SO-02C はソツなく良かったんですけど、ストレージが少なすぎで買い替え直前は容量不足で挙動不審でした。SH-01F は性能良いものの、電池がなくなるのが早く、本体が熱すぎでした。この機種で懲りて Android ハイエンド機を買わなくなりました。

今になって調べてみたところ、この 2機種はマシな部類だったようで、富士通 ARROWS のように「カイロ機能搭載」「電話ができない」「メールがこない」など、怨嗟にまみれたレビューが未だに残っている機種もあります。悲惨です。

日本だけ異常に iPhone 普及率が高い理由って、国内メーカーが 2010 年代初頭にやらかしたから……!?と思ってしまいました……。

メモ: 技術系の話は Facebook から転記しておくことにした。

編集者: すずき(更新: 2021年 10月 8日 16:56)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



こんてんつ

open/close wiki
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報