Vengineerの妄想(準備期間)

人生は短いけど、長いです。人生を楽しみましょう!

Bluespec SystemVerilogのbluesim kernelの中身と生成されたC++モデルの関係

はじめに

  • Bluespec SystemVerilog の例題の中を調べる(その1)
  • Bluespec SystemVerilog の例題の中を調べる(その2)
  • Bluespec SystemVerilog の例題の中を調べる(その3)

の3回の中で、Bluesim での C++モデルを使ってシミュレーションする時、そのシミュレーションカーネルはどうなっているかを調べます。

bsim_standalone

Bluesim の Standalone のシミュレータのテンプレートがここにありました。下記がmain関数です。

int main(int argc, char *argv[])
{
  int status;
  tStatus s;
  tBool b;

  char *p_vcdfile = NULL;

  status = parse_cmd_line_args (argc, argv, & p_vcdfile);
  if (status != 0)
      exit (EXIT_FAILURE);

  tModel model = new_MODEL_BLUESIM_TOP_MODULE();
  if (model == NULL) {
      fprintf (stderr, "ERROR: Cannot create model\n");
      exit (EXIT_FAILURE);
  }
  tSimStateHdl sim = bk_init(model, true);
  if (sim == NULL) {
      fprintf (stderr, "ERROR: Cannot initialize model\n");
      exit (EXIT_FAILURE);
  }

  if (p_vcdfile != NULL) {
      s = bk_set_VCD_file(sim, p_vcdfile);
      if (s != BK_SUCCESS) {
      fprintf (stderr, "ERROR: Cannot set VCD filename\n");
      exit (EXIT_FAILURE);
      }
      b = bk_enable_VCD_dumping(sim);
      if (! b) {
      fprintf (stderr, "ERROR: Cannot enable VCD dumping\n");
      exit (EXIT_FAILURE);
      }
  }

  s = bk_advance(sim, false);
  if (s != BK_SUCCESS) {
      fprintf (stderr, "ERROR: Failure to advance clocks\n");
      exit (EXIT_FAILURE);
  }

  bk_shutdown(sim);

  exit (EXIT_SUCCESS);
}

main関数の中では下記のステップで実行しています。

  • tModel model = new_MODEL_BLUESIM_TOP_MODULE
  • tSimStateHdl sim = bk_init(model, true)
  • s = bk_advance(sim, false)
  • bk_shutdown(sim)

new_MODEL_BLUESIM_TOP_MODULE の部分は、例題で出てきた、下記のmodel_mkFibOne.cxx の new_MODEL_mkFibOne に相当する部分だと思います。

* Function for creating a new model */
void * new_MODEL_mkFibOne()
{
  MODEL_mkFibOne *model = new MODEL_mkFibOne();
  return (void *)(model);
}

bk_init にて、シミュレーションカーネルを初期化し、bk_advanceにて、シミュレーションを実行し、bk_shutdownにてシミュレーションカーネルの後処理を行っています。

bk_init にて、get_version, get_create_time, create_model メソッドを実行しています。

  tSimStateHdl simHdl = new tSimState;

  simHdl->model = (Model*)model;

.....

  tBluesimVersionInfo version;
  simHdl->model->get_version(&(version.name), &(version.build));
  version.creation_time = simHdl->model->get_creation_time();

.....

  simHdl->model->create_model(simHdl, master != 0);

例題における get_version, get_create_time, create_model メソッドは、下記のようになっています。クロックの定義と schedule_posedge_CLK を登録しています。

/* Fill in version numbers */
void MODEL_mkFibOne::get_version(char const **name, char const **build)
{
  *name = NULL;
  *build = NULL;
}
/* Get the model creation time */
time_t MODEL_mkFibOne::get_creation_time()
{
  
  /* Thu Jan  1 00:00:00 UTC 1970 */
  return 0llu;
}
void MODEL_mkFibOne::create_model(tSimStateHdl simHdl, bool master)
{
  sim_hdl = simHdl;
  init_reset_request_counters(sim_hdl);
  mkFibOne_instance = new MOD_mkFibOne(sim_hdl, "top", NULL);
  bk_get_or_define_clock(sim_hdl, "CLK");
  if (master)
  {
    bk_alter_clock(sim_hdl, bk_get_clock_by_name(sim_hdl, "CLK"), CLK_LOW, false, 0llu, 5llu, 5llu);
    bk_use_default_reset(sim_hdl);
  }
  bk_set_clock_event_fn(sim_hdl,
            bk_get_clock_by_name(sim_hdl, "CLK"),
            schedule_posedge_CLK,
            NULL,
            (tEdgeDirection)(POSEDGE));
  (mkFibOne_instance->set_clk_0)("CLK");
}

schedule_posedge_CLK メソッドは、かきのようになっています。

  • INST_top.RL_fib()
  • do_reset_ticks(simHdl) が true の時は、INST_TOP.INST_this_fib_inst と INST_TOP.INST_next_fib_inst を 1 に初期化
static void schedule_posedge_CLK(tSimStateHdl simHdl, void *instance_ptr)
       {
     MOD_mkFibOne &INST_top = *((MOD_mkFibOne *)(instance_ptr));
     tUInt8 DEF_INST_top_DEF_CAN_FIRE_RL_fib;
     tUInt8 DEF_INST_top_DEF_WILL_FIRE_RL_fib;
     DEF_INST_top_DEF_CAN_FIRE_RL_fib = (tUInt8)1u;
     DEF_INST_top_DEF_WILL_FIRE_RL_fib = DEF_INST_top_DEF_CAN_FIRE_RL_fib;
     if (DEF_INST_top_DEF_WILL_FIRE_RL_fib)
       INST_top.RL_fib();
     if (do_reset_ticks(simHdl))
     {
       INST_top.INST_this_fib_inst.rst_tick__clk__1((tUInt8)1u);
       INST_top.INST_next_fib_inst.rst_tick__clk__1((tUInt8)1u);
     }
       };

INST_top.RL_fib() は、mkFibOneクラスのRL_fibメソッドで、下記のようになっています。FibOne の fib ルールの部分です。

void MOD_mkFibOne::RL_fib()
{
  tUInt32 DEF_this_fib_inst_PLUS_next_fib_inst___d3;
  tUInt8 DEF_NOT_this_fib_inst_SLE_10000___d6;
  tUInt32 DEF_b__h131;
  tUInt32 DEF_b__h163;
  tUInt32 DEF_signed_this_fib_inst___d4;
  DEF_b__h163 = INST_this_fib_inst.METH_read();
  DEF_signed_this_fib_inst___d4 = DEF_b__h163;
  DEF_b__h131 = INST_next_fib_inst.METH_read();
  DEF_NOT_this_fib_inst_SLE_10000___d6 = !primSLE8(1u, 32u, (tUInt32)(DEF_b__h163), 32u, 10000u);
  DEF_this_fib_inst_PLUS_next_fib_inst___d3 = DEF_b__h163 + DEF_b__h131;
  INST_this_fib_inst.METH_write(DEF_b__h131);
  INST_next_fib_inst.METH_write(DEF_this_fib_inst_PLUS_next_fib_inst___d3);
  if (!(PORT_RST_N == (tUInt8)0u))
  {
    dollar_display(sim_hdl, this, "s,-32", &__str_literal_1, DEF_signed_this_fib_inst___d4);
    if (DEF_NOT_this_fib_inst_SLE_10000___d6)
      dollar_finish(sim_hdl, "32", 0u);
  }
}

bk_init内で下記のように、sim_thread を pthread にて起動しています。

  /* start the simulation thread and wait for it to block in pause_sim */
  simHdl->sim_running = true;
  pthread_create(&(simHdl->sim_thread_id), NULL, sim_thread, (void*)simHdl);
  wait_for_sim_stop(simHdl);

sim_thread のは、下記のようになっています。

static void* sim_thread(void* ptr)
{
  tSimState* simHdl = (tSimState*)ptr;

  if ((simHdl == NULL) || (simHdl->queue == NULL))
    return NULL;

  /* install signal handlers to shut down simulation */
  struct sigaction sa;
  sa.sa_flags = 0;
  sa.sa_handler = abort_handler;
  sigemptyset(&sa.sa_mask);
  /* SIGINT (user types Ctrl-C) */
  sigaction(SIGINT, &sa, NULL);
  /* SIGPIPE (usually stdout piped to a program that exits, eg /usr/bin/head) */
  sigaction(SIGPIPE, &sa, NULL);

  /* add this sim to the signal watch list */
  add_abort_watcher(simHdl);

  while (!simHdl->sim_shutting_down)
  {
    /* yield to the UI and wait for a trigger to execute */
    lock_sim_state(simHdl);
    simHdl->sim_running = false;
    unlock_sim_state(simHdl);
    pause_sim(simHdl);

    /* execute the events in the simulation queue */
    simHdl->force_halt = false;
    while (bk_is_running(simHdl) && !simHdl->sim_shutting_down)
      simHdl->queue->execute(simHdl);
  }

  /* remove this sim from the signal watch list */
  remove_abort_watcher(simHdl);

  pthread_exit(NULL);
}

下記の部分が実行部分になります。executeが実際に実行している部分です。

  while (!simHdl->sim_shutting_down)
  {
    /* yield to the UI and wait for a trigger to execute */
    lock_sim_state(simHdl);
    simHdl->sim_running = false;
    unlock_sim_state(simHdl);
    pause_sim(simHdl);

    /* execute the events in the simulation queue */
    simHdl->force_halt = false;
    while (bk_is_running(simHdl) && !simHdl->sim_shutting_down)
      simHdl->queue->execute(simHdl);
  }

おわりに

シミュレーションカーネルを眺めたことにより、生成されたC++モデル内のメソッドがどのように呼ばれるのかが何となくわかりました。