FPGA開発日記

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

LiteXによるSoC環境構築を試行する (シリアルコンソールの解析)

https://raw.githubusercontent.com/enjoy-digital/litex/master/doc/litex.png

まず、デザイン上にはシリアルコンソールとして以下のインタフェースが定義されている。

//------------------------------------------------------------------------------
// Module
//------------------------------------------------------------------------------

module sim (
    output wire          sim_trace,
    input  wire          sys_clk,
    output wire          serial_source_valid,
    input  wire          serial_source_ready,
    output wire    [7:0] serial_source_data,
    input  wire          serial_sink_valid,
    output wire          serial_sink_ready,
    input  wire    [7:0] serial_sink_data
);

おそらく、serial_sink_xxxがシリアルの入力で、serial_source_xxxがシリアルの出力になるのだろう。

このインタフェースは、以下のC言語の実装により接続されているように見える。

  • build/sim/gateware/sim_init.cpp
extern "C" void litex_sim_init(void **out)
{
...
    serial[0].signal = &sim->serial_source_valid;
    serial[1].signal = &sim->serial_source_ready;
    serial[2].signal = &sim->serial_source_data;
    serial[3].signal = &sim->serial_sink_valid;
    serial[4].signal = &sim->serial_sink_ready;
    serial[5].signal = &sim->serial_sink_data;
    litex_sim_register_pads(serial, (char*)"serial", 0);
}

この時の、litex_sim_register_pads()はLiteXのシミュレーション環境に、このシリアルデバイスをパッドとして登録する、ということだと思う。

おそらくこのイベントはserial2consoleが管理している。

  • litex/build/sim/core/modules/serial2console/serial2console.c
static int serial2console_add_pads(void *sess, struct pad_list_s *plist)
{
  int ret = RC_OK;
  struct session_s *s = (struct session_s*) sess;
  struct pad_s *pads;

  if(!sess || !plist) {
    ret = RC_INVARG;
    goto out;
  }
  pads = plist->pads;
  if(!strcmp(plist->name, "serial")) {
    litex_sim_module_pads_get(pads, "sink_data", (void**)&s->rx);
    litex_sim_module_pads_get(pads, "sink_valid", (void**)&s->rx_valid);
    litex_sim_module_pads_get(pads, "sink_ready", (void**)&s->rx_ready);
    litex_sim_module_pads_get(pads, "source_data", (void**)&s->tx);
    litex_sim_module_pads_get(pads, "source_valid", (void**)&s->tx_valid);
    litex_sim_module_pads_get(pads, "source_ready", (void**)&s->tx_ready);
  }

  if(!strcmp(plist->name, "sys_clk"))
    litex_sim_module_pads_get(pads, "sys_clk", (void**) &s->sys_clk);

out:
  return ret;
}

入力を受け付けるイベントの生成はこの辺。

static int serial2console_new(void **sess, char *args)
{
  int ret = RC_OK;
  struct timeval tv = {1, 0};
  struct session_s *s = NULL;

  if(!sess) {
    ret = RC_INVARG;
    goto out;
  }

  s = (struct session_s*) malloc(sizeof(struct session_s));
  if(!s) {
    ret=RC_NOENMEM;
    goto out;
  }
  memset(s, 0, sizeof(struct session_s));
  s->ev = event_new(base, fileno(stdin), EV_READ | EV_PERSIST , event_handler, s);
  event_add(s->ev, &tv);

out:
  *sess = (void*) s;
  return ret;
}

イベントハンドラにprintf()を入れてみても、正しく動作しているように見える。 そうすると、何かしらハードウェア側の動作がおかしい、ということになるか?