次のページ 前のページ 目次へ

2. ハードウェアによるバイトデータの転送の仕方

以下はこの話題についての入門的な解説ですが、もっと進んだ内容につい ては FIFO を見てください。

2.1 送信

送信(transmitting)とは、シリアルポートを通じてコンピュータからバイ ト列を送ることです。一旦、送信を理解できれば、受信は同じようなものなの で理解するのは容易です。 ここで最初に示す例は極端に簡略化したものです。 もっと詳しい説明を後で追加します。 コンピュータが(外部ケーブルに繋がっている)シリアルポートから 1 バイト のデータを送ろうとする時、CPU はコンピュータ内部のバス上にあるそのデータをシリアルポートの I/O アド レスに送ります。シリアルポートはそのデータを受け取り、シリアルケーブルの端子の 送信ピンを使ってこれを 1 ビットずつ送ります(シリアルビットストリーム)。 あるビット(とバイト)が電気的にはどう見えるのかについては、 電圧の波形の章をご覧ください。

上記の説明をもう少し詳しくして(まだ完全には程遠いものですが)繰り返 します。シリアルポートで行われる処理のほとんどは UART (または同等のも の)が行います。1 バイトのデータを転送するために、シリアルデバイスドラ イバプログラム(CPU 上で動作)はシリアルポートの I/O アドレスに 1 バイト を送ります。このデータはシリアルポートの「送信シフトレジスタ」(大きさ が 1 バイト)に入ります。このシフトレジスタでは、このバイトデータから 1 ビットずつ取り出され、1 ビットずつシリアル線に送られます。そして最後の ビットが送信され、シフトレジスタが送るための別のバイトを必要とするよう になると、UART は別のバイトデータを送るよう CPU に求めるだけで済みます。 これなら単純な話ですが、CPU がバイトデータを即座に取得することができな いために遅延が生じるかもしれません。なにしろ CPU はシリアルポートの 処理以外にも作業をこなしているのが普通です。

このような遅延をなくす方法は、シフトレジスタがバイトデータを必要とする 前に CPU がバイトデータを取得して、これをシリアルポートの(ハードウェア) バッファに格納することです。 こうしておけば、シフトレジスタがバイトデータを送り出してしまって次に送る バイトデータが即座に必要になった時には、シリアルポートのハードウェアは 自分のバッファに入っている次の バイトデータをシフトレジスタに転送するだけです。 新しいバイトデータを取得するために CPU を呼ぶ必要はありません。

シリアルポートのバッファのサイズは元々 1 バイトしかありませんでしたが、 現在は普通 16 バイトです(高価なシリアルポートにはもっとあります)。しか しそれでも、シフトレジスタが転送するデータを必要とした時に必ずバッファ にデータがあるように、このバッファに十分なバイトデータを与え続けるとい う問題が残っています(送るデータがもう残っていない場合は除きます)。これ は割り込みを使って CPU と連絡することで行います。

まずはバッファが 1 バイトの古い方式のシリアルポートについて説明します。 というのも、バッファが 16 バイトのものも同じように動作するからです( ただし動作はより複雑です)。 シフトレジスタがバイトデータをバッファから取り出し、バッファが他のバイ トデータを必要とすると、コンピュータのバス上にある専用の配線の電圧を上 げることにより、CPU に割り込みが送られます。CPU が非常に重要な処理をし ていなければ、CPU が実行中の処理は割り込みにより強制的に中断され、シリ アルポートのバッファに別のバイトデータを与えるためのプログラムが実行さ れます。このバッファの目的は、シリアルポートのケーブルから送り出される バイトデータが途切れないように、追加のバイトデータが(送られるのを待っ て)ハードウェア内のキューに入った状態を保つことです。

いったん CPU に割り込みがかかると、CPU は誰が割り込みをかけたかを知ります。 なぜなら、各シリアルポートについて専用の割り込み線があるから です(ただし割り込みの共有をしていない場合)。それから CPU はシリアルデ バイスドライバを動作させます。ドライバは I/O アドレスにあるレジスタを チェックし、何が起きたのかを調べます。そして、シリアルポートの送信バッ ファが空になり、追加のバイトデータを待っていることを知ります。したがっ て、送るべきバイトデータがもっとあれば、CPU は次のバイトデータをシリア ルポートの I/O アドレスに送ります。このバイトは、前のバイトがまだ送信 シフトレジスタ内にあり、1 ビットずつ送られている間に届かなくてはなりま せん。

復習すると、1 バイトがシリアルポートの送信線へ完全に送り出されると、 シフトレジスタは空になり、以下の 3 つの動作がほとんど同時に起こります:

  1. 次のバイトが送信バッファから送信シフトレジスタに入れられ ます。
  2. この新しいバイトの(1 ビットずつの)送信が始まります。
  3. 次の割り込みが発行され、たった今、空になった送信バッファへ次の バイトを送るよう、デバイスドライバに指示します。

このように、シリアルポートは割り込み駆動(interrupt driven)であると言えます。 シリアルポートが割り込みを発行する度に、CPU は次のバイトを送ります。 CPU が 1 バイトを送信バッファに送ると、次の割り込みを受け取るまで、CPU は他の処理を自由に行うことができます。シリアルポートはユーザ(あるいは アプリケーションプログラム)が選択した 固定の速度でビット列を送信します。この速度はボーレートと呼ばれることも あります。シリアルポートはバイトごとに追加のビット(スタートビット、ス トップビット、場合によってはパリティビットも)も付け加えるので、多 くの場合、1 バイトごとに 10 ビットのデータが送られます。したがって、通 信レート(速度とも言われます) 19,200 ビット/秒(bps, bit per second)は 1,920 バイト/秒(1,920 割り込み/秒)ということになります。

この処理を全て行うことは CPU にとっても重い負担です。これは色々な理由 から言えます。まずは、32 ビット(64 ビットのことだってあります)のバス上 で一回に 1 バイト(8 ビット)のデータしか送らないので、バス幅をあまり有 効に使っているとは言えません。また、割り込みを送る度に大きなオーバーヘッ ドが生じます。割り込みを受け取った場合でも、デバイスドライバに分かるこ とは何かがシリアルポートで割り込みを起こしたことだけであり、文字が送ら れたことが理由であることまでは分かりません。何が起こったかを調べるには、 デバイスドライバは色々なチェックを行わなければなりません。同じ割り込み が起こっても、文字を受け取ったのかもしれませんし、制御線の状態が変化し たのかもしれません。

重要な解決方法の一つは、シリアルポートのバッファの大きさを 1 バイトか ら 16 バイトに増やすことでした。 つまり、CPU は割り込みを受け取ると、シリアルポート に 16 バイトまでのデータを新たに送ることができます。これにより割り込み の発行は減りますが、太いバスにもかかわらず 1 バイトずつしかデータを送 れない問題はそのままです。16 バイトのバッファは実際には FIFO (First In First Out, 先入れ先出し)のキューであり、よく FIFO と呼ばれます。 FIFO の詳細については FIFO をご覧ください。 これまでの説明も一部繰り返してあります。

2.2 受信

シリアルポートによるバイト列の受信は送信とほぼ同じで、方向が逆になっ ているだけです。受信も割り込み駆動です。受信バッファを 1 バイトしか持 たない古い型のシリアルポートでは、外部ケーブルから 1 バイトのデータ全体を受け取ると、こ のデータは(大きさが 1 バイトである)受信バッファに入ります。するとシリ アルポートは CPU に割り込みをかけてそのデータを取り込ませ、現在受信中 の次のデータを格納できる場所が空くようにします。16 バイトのバッファを持っ ている新しいシリアルポートの場合は、この(バイトデータを取得するための) 割り込みはバッファに 14 バイトのデータが蓄積した時点で発行されます。す ると CPU は現在の処理を一旦止め、14 から 16 バイトのデータをシリアルポー トから取り出します。14 番目のバイトが受け取られた時に送られた割り込み に対し、これが起きてからさらに 2 つのバイトデータが届くと、受け取るべ きバイトの個数は 16 バイトになるかもしれません。しかし(2 バイトではな く) 3 バイト届いてしまうと、16 バイトのバッファは溢れてしまいます。 14 バイト未満のデータを取り込むことも、そのように設定することや タイムアウトによりできます。詳しくは FIFO を見 てください。

2.3 シリアルポートの大きなバッファ

今まではシリアルポートが持っている 16 バイトの小さいハード ウェアバッファについて説明しましたが、メインメモリにはもっと大きな バッファもあります。CPU はいくつかのバイトデータを ハードウェアの受信バッファから取り込むと、これらのデータをメイン メモリ中のずっと大きな(例えば 8K バイトの)バッファに入れます。したがって、 シリアルポートからデータを受け取るプログラムは、この大きなバッファから (プログラム中で "read" 文を用いて)データを取り出します。送信するデータ についても同じことが言えます。CPU がデータをいくらか送る必要がある場合、 CPU はメインメモリにある大きい(8K バイトの)送信バッファからデータを取 り出し、ハードウェアが持つ 16 バイトの小さな送信バッファに入れます。


次のページ 前のページ 目次へ