SystemC/TLM: blocking & non-blocking transport

在systemC建模中,对于initiator socket 和 target socket 之间的payload信息交互,有blocking 和 non blocking transport的区别。Spec中有如下的解释。

The blocking transport interface is only able to model the start and end of a transaction, with the transaction being completed within a single function call. The non-blocking transport interface allows a transaction to be broken down into multiple timing points, and typically requires multiple function calls for a single transaction.

一般情况下,blocking transport用于function module建模,non-blocking transport 一般用于 performance module 建模。

对于blocking transport ,slave Module中,需要在构造函数中 对target socket 注册b_transport 函数。实际执行中,Master Module通过initiator socket 调用 b_transport 函数 来将payload信息传递给slave Module。调用 b_transport 函数结束,表明本次 request 成功发送给slave,并且slave成功将 response信息传递给了master。

 对于non-blocking transport,slave Module中,需要在构造函数中 对target socket 注册nb_transport_fw 函数; master Module中,需要在构造函数中 对initiator socket 注册nb_transport_bw 函数。tlm phase用于标识non-blocking transport过程中的状态。tlm_core/tlm_2/tlm_generic_payload/tlm_phase.h 中定义了四种最基本的phase : BEGIN_REQ=1, END_REQ, BEGIN_RESP, END_RESP。下图是一个模拟两个channel (request 和 response)的协议接口间交互示意图。同时,nb_transport_bw中需要对接收的phase为END_REQ和BEGIN_RESP 的情况进行分类处理;nb_transport_fw则需要对接收到的phase为BEGIN_REQ和END_RESP进行处理。

一般情况下,在target不阻塞时,target收到BEGIN_REQ phase后,当拍就会调用initiator的nb_transport_bw,发送END_REQ phase。如果需要模拟request方向target 对initiator的反压,则可以在收到BEGIN_REQ phase后,wait block logic,阻塞解除后,再发送END_REQ phase。一般在 nb_transport_fw和 nb_transport_bw函数中不加显式或隐式的wait。

如果master和slave之间的交互协议较为复杂,可以通过TLM_DECLARE_EXTENDED_PHASE 来增加 phase。比如要模拟AXI 协议的5个channel,则可以新增 BEGIN_ { AXI_AW / AXI_AR / AXI_W / AXI_R / AXI_B } 和 END_ { AXI_AW / AXI_AR / AXI_W / AXI_R / AXI_B } 这 10个phase。

 

一个 blocking transport的示例如下。需要注意,slave Module (代码中为class MyTarget) 中注册的 b_transport函数(代码中为 RecvReqFunc)必须为这样的形式void (tlm::tlm_generic_payload &payload, sc_core::sc_time &delay_time),两个参数必须是payload & 和 sc_time &。

// execute:
//     g++ -g -Wall -lsystemc -m64 -pthread main.cpp
//         -L/$(your systemc path)/lib-linux64
//         -I/$(your systemc path)/include  -I/$(your systemc
//         path)/src/tlm_utils -o sim

#include <systemc>

#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/simple_target_socket.h"

class MyInitiator : public sc_core::sc_module
{
public:
  SC_HAS_PROCESS(MyInitiator); // must have this line
  explicit MyInitiator(sc_core::sc_module_name name)
      : sc_core::sc_module(name),
        m_initiator_port("initiator_port")
  {
    SC_THREAD(MainThread);
  }

  void MainThread()
  {
    int t_cycle_cnt = 0;
    sc_core::sc_time t_delay = sc_core::SC_ZERO_TIME;
    while (1)
    {
      t_cycle_cnt++;
      t_delay = sc_core::sc_time(t_cycle_cnt, sc_core::SC_NS);
      tlm::tlm_generic_payload *t_payload = new tlm::tlm_generic_payload();
      t_payload->set_address(0x10000 + t_cycle_cnt);
      std::cout << "\033[34m [" << sc_core::sc_time_stamp() << "]"
                << " call b_transport " << std::hex << t_payload->get_address()
                << " delay cycle " << t_cycle_cnt
                << " \033[0m" << std::endl;
      m_initiator_port->b_transport(*t_payload, t_delay); // here will call MyTarget::RecvReqFunc
      wait(1, sc_core::SC_NS);
    }
  }

  tlm_utils::simple_initiator_socket<MyInitiator> m_initiator_port;
};

class MyTarget : public sc_core::sc_module
{
public:
  SC_HAS_PROCESS(MyTarget); // must have this line
  explicit MyTarget(sc_core::sc_module_name name)
      : sc_core::sc_module(name), m_target_port("target_port")
  {
    m_target_port.register_b_transport(this, &MyTarget::RecvReqFunc);
  }

  //void at_target_2_phase::b_transport(tlm::tlm_generic_payload &payload, sc_core::sc_time &delay_time)
  void RecvReqFunc(tlm::tlm_generic_payload &payload, sc_core::sc_time &delay)
  {
    wait(delay);
    std::cout << "\033[35m [" << sc_core::sc_time_stamp() << "]"
              << " RecvReqFunc " << std::hex << payload.get_address() << " \033[0m" << std::endl;
  }

  tlm_utils::simple_target_socket<MyTarget> m_target_port;
};

class MyTop : public sc_core::sc_module
{
public:
  SC_HAS_PROCESS(MyTop);
  explicit MyTop(sc_core::sc_module_name name)
      : sc_core::sc_module(name),
        m_init("init"),
        m_target("target")
  {
    m_init.m_initiator_port.bind(m_target.m_target_port);
  }

  MyInitiator m_init;
  MyTarget m_target;
};

int sc_main(int argc, char **argv)
{
  MyTop m_top_module("my_top_module");
  sc_core::sc_start(20, sc_core::SC_NS);
  return 0;
}

nb_transport 一个示例如下。 

// execute:
//     g++ -g -Wall -lsystemc -m64 -pthread main.cpp
//         -L/$(your systemc path)/lib-linux64
//         -I/$(your systemc path)/include  -I/$(your systemc
//         path)/src/tlm_utils -o sim

#include <systemc>

#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/simple_target_socket.h"
#include "tlm_utils/peq_with_get.h"

class MyInitiator_Nb : public sc_core::sc_module
{
public:
  SC_HAS_PROCESS(MyInitiator_Nb); // must have this line
  explicit MyInitiator_Nb(sc_core::sc_module_name name)
      : sc_core::sc_module(name),
        m_initiator_port("initiator_port"), m_test_peq("peq")
  {
    m_initiator_port.register_nb_transport_bw(this, &MyInitiator_Nb::nb_transport_bw_func);
    SC_THREAD(SendReqThread);
    SC_THREAD(SendEndRespThread);
    sensitive << m_test_peq.get_event(); // sensitive event list
  }

  void SendReqThread()
  {
    int t_cycle_cnt = 0;
    tlm::tlm_phase t_phase = tlm::BEGIN_REQ;
    sc_core::sc_time t_delay = sc_core::SC_ZERO_TIME;
    while (1)
    {
      t_cycle_cnt++;
      t_delay = sc_core::sc_time(t_cycle_cnt, sc_core::SC_NS);
      tlm::tlm_generic_payload *t_payload = new tlm::tlm_generic_payload();
      t_payload->set_address(0x10000 + t_cycle_cnt);
      std::cout << this->name() << "\033[34m [" << sc_core::sc_time_stamp() << "]"
                << " call nb_transport_fw, BEGIN_REQ phase, addr=0x" << std::hex << t_payload->get_address()
                << " delay cycle " << t_cycle_cnt
                << " \033[0m" << std::endl;
      m_initiator_port->nb_transport_fw(*t_payload, t_phase, t_delay);

      /***********************************/
      // here can add your block logic, 
      // basic block logic: need wait recv END_REQ phase
      wait(m_slv_end_req_evt);
      /***********************************/

      wait(1, sc_core::SC_NS);
    }
  }

  tlm::tlm_sync_enum nb_transport_bw_func(tlm::tlm_generic_payload &payload,
                                          tlm::tlm_phase &phase, sc_core::sc_time &delay)
  {
    switch (phase)
    {
    case tlm::END_REQ:
      std::cout << this->name()
                << "\033[35m [" << sc_core::sc_time_stamp() << "]"
                << " nb_transport_bw_func recv END_REQ phase, addr=0x" << std::hex << payload.get_address()
                << " \033[0m" << std::endl;
      m_slv_end_req_evt.notify();
      break;
    case tlm::BEGIN_RESP:
      std::cout << this->name() << "\033[35m [" << sc_core::sc_time_stamp() << "]"
                << " nb_transport_bw_func recv BEGIN_RESP phase, addr=0x" << std::hex << payload.get_address()
                << " \033[0m" << std::endl;
      m_test_peq.notify(payload);
      break;
    default:
      assert(false);
    }
    return tlm::TLM_ACCEPTED;
  }

  void SendEndRespThread()
  {
    tlm::tlm_generic_payload *t_get = NULL;
    tlm::tlm_phase t_phase = tlm::END_RESP;
    sc_core::sc_time t_delay = sc_core::SC_ZERO_TIME;
    while (1)
    {
      wait(); // wait sensitive event list

      // here must get next transaction entil t_get is NULL
      while ((t_get = m_test_peq.get_next_transaction()) != NULL)
      {
        std::cout << this->name() << "\033[34m [" << sc_core::sc_time_stamp() << "]"
                  << " call nb_transport_fw, END_RESP phase, addr=0x" << std::hex << t_get->get_address()
                  << " \033[0m" << std::endl;
        m_initiator_port->nb_transport_fw(*t_get, t_phase, t_delay);
        t_get = NULL;

        // in this block, must can't wait any event or cycle delay
        //  if not, the time of transaction obtained will not accurate
      }
    }
  }

  sc_core::sc_event m_slv_end_req_evt;
  tlm_utils::simple_initiator_socket<MyInitiator_Nb> m_initiator_port;
  tlm_utils::peq_with_get<tlm::tlm_generic_payload> m_test_peq;
};

class MyTarget_Nb : public sc_core::sc_module
{
public:
  SC_HAS_PROCESS(MyTarget_Nb); // must have this line
  explicit MyTarget_Nb(sc_core::sc_module_name name)
      : sc_core::sc_module(name), m_target_port("target_port_nb")
  {
    m_target_port.register_nb_transport_fw(this, &MyTarget_Nb::nb_transport_fw_func);

    SC_THREAD(MainThread);
    SC_THREAD(BeginRespThread);
  }

  tlm::tlm_sync_enum nb_transport_fw_func(tlm::tlm_generic_payload &payload,
                                          tlm::tlm_phase &phase, sc_core::sc_time &delay)
  {
    wait(delay); // here can add wait logic, also can delete wait & ignore the delay
    switch (phase)
    {
    case tlm::BEGIN_REQ:
      m_req_fifo.write(&payload);
      std::cout << this->name()
                << "\033[35m [" << sc_core::sc_time_stamp() << "]"
                << " nb_transport_fw_func recv BEGIN_REQ phase, addr=0x" << std::hex << payload.get_address()
                << " \033[0m" << std::endl;
      break;
    case tlm::END_RESP:
      std::cout << this->name() << "\033[35m [" << sc_core::sc_time_stamp() << "]"
                << " nb_transport_fw_func recv END_RESP phase, addr=0x" << std::hex << payload.get_address()
                << " \033[0m\n"
                << std::endl;
      break;
    default:
      assert(false);
    }
    return tlm::TLM_ACCEPTED;
  }

  void MainThread()
  {
    tlm::tlm_phase t_phase = tlm::END_REQ;
    sc_core::sc_time t_delay = sc_core::SC_ZERO_TIME;
    while (1)
    {
      tlm::tlm_generic_payload *t_payload = m_req_fifo.read();

      /***********************************/
      // here can add your block logic, for back pressure use
      /***********************************/

      std::cout << this->name() << "\033[38m [" << sc_core::sc_time_stamp() << "]"
                << " call nb_transport_bw, END_REQ phase, addr=0x" << std::hex << t_payload->get_address()
                << " \033[0m" << std::endl;
      m_target_port->nb_transport_bw(*t_payload, t_phase, t_delay);

      /***********************************/
      // after END_REQ phase, indicate slave recv req successfully,
      // then handle req, return BEGIN_RESP
      /***********************************/
      m_resp_fifo.write(t_payload);

      wait(1, sc_core::SC_NS);
    }
  }

  void BeginRespThread()
  {
    tlm::tlm_phase t_phase = tlm::BEGIN_RESP;
    // sc_core::sc_time t_delay = sc_core::SC_ZERO_TIME;
    sc_core::sc_time t_delay = sc_core::sc_time(1, sc_core::SC_NS);
    while (1)
    {
      tlm::tlm_generic_payload *t_payload = m_resp_fifo.read();
      std::cout << this->name() << "\033[38m [" << sc_core::sc_time_stamp() << "]"
                << " call nb_transport_bw, BEGIN_RESP phase, addr=0x" << std::hex << t_payload->get_address()
                << " \033[0m" << std::endl;
      m_target_port->nb_transport_bw(*t_payload, t_phase, t_delay);

      wait(1, sc_core::SC_NS);
    }
  }

  tlm_utils::simple_target_socket<MyTarget_Nb> m_target_port;
  sc_core::sc_fifo<tlm::tlm_generic_payload *> m_req_fifo;
  sc_core::sc_fifo<tlm::tlm_generic_payload *> m_resp_fifo;
};

class MyTop : public sc_core::sc_module
{
public:
  SC_HAS_PROCESS(MyTop);
  explicit MyTop(sc_core::sc_module_name name)
      : sc_core::sc_module(name),
        m_init("init"),
        m_target("targ")
  {
    m_init.m_initiator_port.bind(m_target.m_target_port);
  }

  MyInitiator_Nb m_init;
  MyTarget_Nb m_target;
};

int sc_main(int argc, char **argv)
{
  MyTop m_top_module("my_top_module_nb");
  sc_core::sc_start(20, sc_core::SC_NS);
  return 0;
}


 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

123axj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值