Armadilloフォーラム

Armadillo460シリアルとTFTP

yue110

2015年1月19日 16時03分

毎度お世話になっております。
Armadillo460のシリアルについて質問いたします。

現在、シリアルで外部のICと230400bpsで通信しているのですが、通常は問題ないのですが、
その通信とは別で、TFTPで外部のPCへデータを渡す事があります。
(TFTPで送るファイルはCSVファイルで、Max800ファイル程度)

そして、そのTFTPで大量のファイルを送ると、その処理をやっている最中や、その処理の後、
それまで問題なくできていたシリアルが稀にデータを取りこぼす様になってしまいます。

試しに115200bpsにして試してみたところ、同じことをしても問題は出ませんでした。
また、どのようにおかしくなっているのか確認したところ、
受信データが数バイト足りない(何バイトか正常に取れていない)というような状態でした。

ということは、シリアルがクロックからずれてしまっているなど、何かしらの問題で
フレーミングエラー、オーバーランエラーといったエラーが発生し、正常に取れていないのではと思いました。
(相手の外部ICは、シリアルの誤差率は0%で設定されています。)

これを解消する方法は何かないでしょうか?
また、もし、フレーミングエラー、オーバーランエラーといったエラーが発生しているのだとしたら、
それを確認する方法はないでしょうか?

分かりましたら教えていただけないでしょうか?
よろしくお願い致します。

コメント

y.nakamura

2015年1月19日 21時00分

中村です。

まず、UARTを使った通信は信頼性が低いものですので、
アプリレベルでエラー検出・エラー訂正・再送処理などが必要である、
ということは理解していらっしゃるという前提で・・・

> そして、そのTFTPで大量のファイルを送ると、その処理をやっている最中や、その処理の後、
> それまで問題なくできていたシリアルが稀にデータを取りこぼす様になってしまいます。
> 試しに115200bpsにして試してみたところ、同じことをしても問題は出ませんでした。

CPUの処理能力を超えたのでしょう。

> また、どのようにおかしくなっているのか確認したところ、
> 受信データが数バイト足りない(何バイトか正常に取れていない)というような状態でした。

データの取りこぼしは2か所で発生します。
1. ハードウェアの受信FIFOからドライバがデータを読み出すところ
2. ドライバの受信バッファからアプリがデータを読み出すところ
(2はドライバが受信バッファにデータを入れるところ、と考えてもいい)

今回はこのどちらかになっているのだと思います。

> フレーミングエラー、オーバーランエラーといったエラーが発生し、正常に取れていないのではと思いました。

オーバランエラーの可能性が高いですね。
これは上の1でのエラーです。

> これを解消する方法は何かないでしょうか?

原因が上の2の場合には、通信相手とフロー制御をすることで
多くの場合は改善できます。

原因が上の1の場合には・・・
400シリーズのUARTドライバ(mxc_uart.c)には、いろいろと問題があります。
先日(昨年の暮れごろ)ある人(アットマークテクノさんの中の人ではなく
外の人です)と話をしたときに、だいぶ前から指摘があるのに直ってない、
というようなことを言っていました。
その指摘は私がしたものなのですが、CPU負荷が上がったときに問題に
なるような部分もいくつかありました。
それを直せば少しは改善するかもしれません。

メーリングリスト時代の[Armadillo:08293]
http://lists.atmark-techno.com/pipermail/armadillo/2012-September/00829…
です。

> また、もし、フレーミングエラー、オーバーランエラーといったエラーが発生しているのだとしたら、
> それを確認する方法はないでしょうか?

drivers/serialにあるmxc_uart.cとserial_core.cをみたところでは、
ioctlのTIOCGICOUNTで取り出せそうです。
(armadilloでこれを使ったことはありませんが...)

TIOCGICOUNTは、include/linux/serial.hで定義されている
次の構造体を使います。

struct serial_icounter_struct {
        int cts, dsr, rng, dcd;
        int rx, tx;
        int frame, overrun, parity, brk;
        int buf_overrun;
        int reserved[9];
};

// mxc_uart.cを読む限りでは、Armadilloではbuf_overrunは
// セット(カウント)していないみたいです。
// これが上の原因2だと思うのですが...

あるいは、mxc_uart.cの次の部分にprintk()などを入れて
簡易的に確認する方法もあります。

static void mxcuart_rx_chars(uart_mxc_port * umxc)
{
        ...
                        } else if (status & MXC_UARTURXD_FRMERR) {
                                umxc->port.icount.frame++;
                        } else if (status & MXC_UARTURXD_PRERR) {
                                umxc->port.icount.parity++;
                        }
                        if (status & MXC_UARTURXD_OVRRUN) {
                                umxc->port.icount.overrun++;
                        }
        ...
}

--
なかむら

yue110

2015年1月19日 21時11分

中村様

回答ありがとうございました。
早速確認してみます。

また何かわからないことなどでてきましたら、ご質問するかもしれません。
よろしくお願い致します。

saitoh

2015年1月21日 10時14分

齊藤と申します。
TFTPというのは、シリアルとは別に有線LANのポートもあってそちらでtftpを使ってるということでしょうか?
だとしたらおそらく割込みの競合でシリアルのハードウェアから着信データを取り出すところで取りこぼしていると思われますね。

手っ取り早い対策はこんなあたりでしょうか。
案1. RTS_CTS ハードウェアフローコントロールを使う(ピン配置が許せば)
案2. Armadillo460のシリアルをあきらめて、USBシリアルをつかうと改善する可能性があります。たとえばPL2303だと256バイトといったように受信バッファの容量が大きいので
案3. ヤマカンですし正統な対策ではないですが、LAN側のMTUを小さくする(これでシリアル割込みが禁止される時間が短くなるかも)

saitoh

2015年1月21日 10時17分

齊藤と申します。
TFTPというのは、シリアルとは別に有線LANのポートもあってそちらでtftpを使ってるということでしょうか?
だとしたらおそらく割込みの競合でシリアルのハードウェアから着信データを取り出すところで取りこぼしていると思われますね。

手っ取り早い対策はこんなあたりでしょうか。
案1. RTS_CTS ハードウェアフローコントロールを使う(ピン配置が許せば)
案2. Armadillo460のシリアルをあきらめて、USBシリアルをつかうと改善する可能性があります。たとえばPL2303だと256バイトといったように受信バッファの容量が大きいので
案3. ヤマカンですし正統な対策ではないですが、LAN側のMTUを小さくする(これでシリアル割込みが禁止される時間が短くなるかも)

y.nakamura

2015年1月21日 10時35分

中村です。

購読者皆様のために...

> だとしたらおそらく割込みの競合でシリアルのハードウェアから着信データを取り出すところで取りこぼしていると思われますね。

これが原因の場合、

> 案1. RTS_CTS ハードウェアフローコントロールを使う(ピン配置が許せば)

これでは解決できません。(効果ありません)

--
なかむら

saitoh

2015年1月21日 10時57分

あれ?
ハードウェアの受信バッファが一杯になった時点でCTSがOFFになって、相手側に送信休止を促すはずですが。
Armadilloでは経験が無いですが,他のHWではさんざんやりました。昔々のアナログモデムで19200についていけなくてハードウェアフローコントロールを使って時代の話なので、今のlinuxでは違うんですかね

y.nakamura

2015年1月21日 22時36分

中村です。

すいません。一般論で書いてしまっていました。

> ハードウェアの受信バッファが一杯になった時点でCTSがOFFになって、相手側に送信休止を促すはずですが。

1つ前の投稿の
> > > 案1. RTS_CTS ハードウェアフローコントロールを使う(ピン配置が許せば)
> > これでは解決できません。(効果ありません)

この部分を訂正します。

i.MX25ではハードの受信FIFO内の受信データ数が
一定レベルを超えるとソフト=ドライバが関与せずに
ハードウェアでRTSをOFFしてますね。
これならば、割り込み処理が間に合わないことが原因の
取りこぼしの対策になります。

> Armadilloでは経験が無いですが,他のHWではさんざんやりました。昔々のアナログモデムで19200についていけなくてハードウェアフローコントロールを使って時代の話なので、今のlinuxでは違うんですかね

Linuxの基本となるシリアルドライバでは、ソフトで(ドライバで)
RTSのON/OFFをしますので、RTS/CTSフロー制御では
割り込みが間に合わないことが原因の取りこぼしは解決できません。
このあたりは今も昔も同じだと思います。

ですが、上で訂正したように、ハードウェアで自動的にRTSを
ON/OFFしてくれるチップの場合は解決できます。
saitohさんが使われていたハードウェアはこういうタイプ
だったのではないでしょうか。

上でLinuxの基本となるシリアルドライバ、と書きましたが、
たとえば、いわゆるDOS/V機、AT互換機の標準のUARTの
ICである8250/16550系のチップは(例外あり<※1>)、
ドライバがRTS/CTSのON/OFFをしてます。

それをやっているのが、serial_core.cのここ。

static void uart_throttle(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;

if (I_IXOFF(tty))
uart_send_xchar(tty, STOP_CHAR(tty));

if (tty->termios->c_cflag & CRTSCTS)
uart_clear_mctrl(state->uart_port, TIOCM_RTS);
}

static void uart_unthrottle(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;

if (I_IXOFF(tty)) {
if (port->x_char)
port->x_char = 0;
else
uart_send_xchar(tty, START_CHAR(tty));
}

if (tty->termios->c_cflag & CRTSCTS)
uart_set_mctrl(port, TIOCM_RTS);
}

<※1>
昔書いていたシリアルドライバのメモをひっぱりだして見ると、
TL16C750は64バイトのFIFOを持っていて、受信FIFOに
一定レベルのデータが溜まると自動でRTSをOFFにする
機能がありました。で、
8250/16550ではどうにもならなかった取りこぼしが、
16550をTL16C750に差し替えて(ドライバもTL16C750に
対応するように書き換えて)解決できたという経験があります。
(PC-9801の拡張シリアルボードでの話です)
と、ここまで書いて、Linuxの8250.cを見てみると、
こんなコードがありました。

static const struct serial8250_config uart_config[] = {
    ...
        [PORT_16750] = {
                .name           = "TI16750",
                .fifo_size      = 64,
                .tx_loadsz      = 64,
                .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
                                  UART_FCR7_64BYTE,
                .flags          = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE,
        },
    ...
};
static void
serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
                       struct ktermios *old)
{
    ...
        /*
         * MCR-based auto flow control.  When AFE is enabled, RTS will be
         * deasserted when the receive FIFO contains more characters than
         * the trigger, or the MCR RTS bit is cleared.  In the case where
         * the remote UART is not using CTS auto flow control, we must
         * have sufficient FIFO entries for the latency of the remote
         * UART to respond.  IOW, at least 32 bytes of FIFO.
         */
        if (up->capabilities & UART_CAP_AFE && up->port.fifosize >= 32) {
                up->mcr &= ~UART_MCR_AFE;
                if (termios->c_cflag & CRTSCTS)
                        up->mcr |= UART_MCR_AFE;
        }
    ...
}

ちょっと話がそれてしまいました。スミマセン。

--
なかむら

yue110

2015年5月22日 16時13分

yue110です。

皆様、色々と記載していただきありがとうございました。
また、色々と記載していただいたのに、あいさつもせず、
こんなに時間経ってしまい申し訳ありませんでした。

あの後、TFTPでの使用予定が変更になり、実はFTPに変更になっており、
問題なく進んでおりました。

そして、バタバタしていたこともあり、この問い合わせをしていたことをスッカリ忘れてしまっておりました。
誠に申し訳ありませんでした。

今後は、忘れないように気を付けていきますので、なにとぞ今後ともよろしくお願い致します。