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での挙動を確認していこうと思う。 ポイントは、データバッファとステートマシンだな。