FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

LiteXのUARTハードウェアの確認 (Migenのソースコードを読む)

LiteXのUARTデバイスの挙動を確認したくて、Migenのソースコードをチェックしている:

UARTのデバイスは、Wishboneを経由して接続されている。

  • litex/litex/soc/cores/uart.py
class UARTWishboneBridge(UARTBone):
    def __init__(self, pads, clk_freq, baudrate=115200, cd="sys"):
        self.phy = RS232PHY(pads, clk_freq, baudrate)
        UARTBone.__init__(self, self.phy, clk_freq, cd)

RS232の送信用のハードウェアと、受信用のハードウェアが接続されている。

class RS232PHY(LiteXModule):
    def __init__(self, pads, clk_freq, baudrate=115200, with_dynamic_baudrate=False):
        tuning_word = int((baudrate/clk_freq)*2**32)
        if with_dynamic_baudrate:
            self._tuning_word  = CSRStorage(32, reset=tuning_word)
            tuning_word = self._tuning_word.storage
        self.tx = RS232PHYTX(pads, tuning_word)
        self.rx = RS232PHYRX(pads, tuning_word)
        self.sink, self.source = self.tx.sink, self.rx.source

TXのほうを確認すると、ステートマシンが作成してある。

        # FSM                                                                                                                                                                                                                                                                                                                                                                             
        self.fsm = fsm = FSM(reset_state="IDLE")
        fsm.act("IDLE",
            # Reset Count and set TX to Idle.                                                                                                                                                                                                                                                                                                                                             
            NextValue(count,   0),
            NextValue(pads.tx, RS232_IDLE),
            # Wait for TX data to transmit.                                                                                                                                                                                                                                                                                                                                               
            If(sink.valid,
                NextValue(pads.tx, RS232_START),
                NextValue(data, sink.data),
                NextState("RUN")
            )
        )
        fsm.act("RUN",
            # Enable Clock Phase Accumulator.                                                                                                                                                                                                                                                                                                                                             
            clk_phase_accum.enable.eq(1),
            # On Clock Phase Accumulator tick:                                                                                                                                                                                                                                                                                                                                            
            If(clk_phase_accum.tick,
                # Set TX data.                                                                                                                                                                                                                                                                                                                                                            
                NextValue(pads.tx, data),
                # Increment Count.                                                                                                                                                                                                                                                                                                                                                        
                NextValue(count, count + 1),
                # Shift TX data.                                                                                                                                                                                                                                                                                                                                                          
                NextValue(data, Cat(data[1:], RS232_STOP)),
                # When 10-bit have been transmitted...                                                                                                                                                                                                                                                                                                                                    
                If(count == (10 - 1),
                    # Ack sink and return to Idle.                                                                                                                                                                                                                                                                                                                                        
                    sink.ready.eq(1),
                    NextState("IDLE")
                )
            )
        )

2つのステートが存在している:

  • IDLE : 入力を受け付けると、pads.txにSTARTコマンド(=0)を打ち込み、dataレジスタに書き込みデータを格納し、RUNステートに遷移する。
  • RUN : clk_phase_accum信号をベースに駆動する。これはRS232のクロックと動作周波数の比率で動作するカウンタで、RS232の信号制御用のカウントを行う。
    • countによりすべての文字列を出力すれば、そこで動作終了。そうでなければ、次のdataビットを出力する。

と、非常にシンプルなステートマシンを組んでいることが分かった。これをベースに、FPGAでの挙動を確認していこうと思う。 ポイントは、データバッファとステートマシンだな。