在之前博客SystemC/TLM: blocking & non-blocking transport_123axj的博客-CSDN博客中讲到了SystemC中的blocking 和 non-blocking transport,需要 initiator 和 target 两边的transport类型一致,也就是都为 blocking transport 或者都为 non-blocking transport。
而假如 initiator 和 target 两边的transport类型不一致时,是否可以正常执行呢?如果 我们使用的协议只有request和response两个channel,只使用BEGIN_REQ, END_REQ, BEGIN_RESP, END_RESP 这几个tlm phase,则答案是可以 ;如果使用的是诸如AXI之类的其他接口协议,则不能。以下主要讨论前者,我们可以借用之前的代码将 MyInitiator 和 MyTarget_Nb 绑定一起(以下称之为 top_module_b2nb),将MyInitiator_Nb 和 MyTarget 绑定一起(以下称之为 top_module_nb2b)。
对于top_module_b2nb,由于 MyTarget_Nb中m_target_port只注册了nb_transport_fw 函数,而没有注册b_transport 函数,所以 MyInitiator 在执行m_initiator_port->b_transport(*t_payload, t_delay); (实际执行的是 tlm_utils/simple_target_socket.h中的 b_transport函数 )的时候,我们发现 b_transport函数中有如下的逻辑,可以得知,m_b_transport_ptr 是空指针,m_nb_transport_ptr是有指向的,此时进行 b2nb 的转换,simple_target_socket.h中的b2nb_thread() 是用于 b2nb转换的sc_thread。
void b_transport(transaction_type& trans, sc_core::sc_time& t)
{
if (m_b_transport_ptr) {
// forward call
sc_assert(m_mod);
(m_mod->*m_b_transport_ptr)(trans, t);
return;
}
// b->nb conversion
if (m_nb_transport_ptr) {
m_peq.notify(trans, t);
….
}
}
对于top_module_nb2b,MyInitiator_Nb 注册了nb_transport_bw,而MyTarget中注册了b_transport. MyInitiator_Nb在执行m_initiator_port->nb_transport_fw(*t_payload, BEGIN_REQ, t_delay); (实际执行的是 tlm_utils/simple_target_socket.h中的 nb_transport_fw函数 )的时候,会发现 m_nb_transport_ptr指针为空,m_b_transport_ptr非空,此时会 spawn一个 nb2b_thread,在nb2b_thread 中,我们发现其直接向initiator 发起 BEGIN_RESP phase,也就是说其 省略了END_REQ phase。
如果我们直接使用 SystemC/TLM: blocking & non-blocking transport_123axj的博客-CSDN博客 中的MyInitiator_Nb 和 My_Target 相连,会导致 MyInitiator_Nb::SendReqThread() 一直在等待 收到END_REQ phase,进而MyInitiator_Nb 不能再发出第2个request。
为此,可以增加一个m_slv_begin_resp_evt,MyInitiator_Nb::SendReqThread()中 wait m_slv_end_req_evt | m_slv_begin_resp_evt,这样MyInitiator_Nb 就可以发出后续多个request 了。
sync_enum_type nb_transport_fw(transaction_type& trans,
phase_type& phase,
sc_core::sc_time& t)
{
if (m_nb_transport_ptr) {
…..
}
// nb->b conversion
if (m_b_transport_ptr) {
if (phase == tlm::BEGIN_REQ) {
…..
sc_core::sc_spawn(sc_bind(&fw_process::nb2b_thread, this, ph),
sc_core::sc_gen_unique_name("nb2b_thread"), &opts);
}
if (phase == tlm::END_RESP) {
….
}
}
void nb2b_thread(process_handle_class* h)
{
while(1) {
….
phase_type phase = tlm::BEGIN_RESP;
sync_enum_type sync = m_owner->bw_nb_transport(*trans, phase, t);
}
}
b2nb 代码实现
// 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() << "]"
<< " begin call b_transport, addr=0x" << std::hex << t_payload->get_address()
<< " delay cycle " << t_cycle_cnt
<< " \033[0m" << std::endl;
m_initiator_port->b_transport(*t_payload, t_delay);
std::cout << "\033[34m [" << sc_core::sc_time_stamp() << "]"
<< " end call b_transport, addr=0x" << std::hex << t_payload->get_address()
<< " delay cycle " << t_cycle_cnt
<< " \033[0m\n" << std::endl;
wait(1, sc_core::SC_NS);
}
}
tlm_utils::simple_initiator_socket<MyInitiator> m_initiator_port;
};
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);
}
// don't recommend add wait logic in nb_transport_fw / nb_transport_bw
tlm::tlm_sync_enum nb_transport_fw_func(tlm::tlm_generic_payload &payload,
tlm::tlm_phase &phase, sc_core::sc_time &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
wait(1, sc_core::SC_NS);
/***********************************/
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("target")
{
m_init.m_initiator_port.bind(m_target.m_target_port);
}
MyInitiator m_init;
MyTarget_Nb m_target;
};
int sc_main(int argc, char **argv)
{
MyTop m_top_module("my_top_module_b2nb");
sc_core::sc_start(20, sc_core::SC_NS);
return 0;
}
nb2b 代码实现
// 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_Nb_1 : public sc_core::sc_module
{
public:
SC_HAS_PROCESS(MyInitiator_Nb_1); // must have this line
explicit MyInitiator_Nb_1(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_1::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);
std::cout << this->name() << "\033[34m [" << sc_core::sc_time_stamp() << "]"
<< " call nb_transport_fw, BEGIN_REQ phase finish, addr=0x" << std::hex << t_payload->get_address()
<< " delay cycle " << t_cycle_cnt
<< " \033[0m" << std::endl;
/***********************************/
// here can add your block logic,
// basic block logic: need wait recv END_REQ phase
wait(m_slv_end_req_evt | m_slv_begin_resp_evt);
/***********************************/
wait(1, sc_core::SC_NS);
}
}
// don't recommend add wait logic in nb_transport_fw / nb_transport_bw
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);
m_slv_begin_resp_evt.notify();
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\n" << 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;
sc_core::sc_event m_slv_begin_resp_evt;
tlm_utils::simple_initiator_socket<MyInitiator_Nb_1> m_initiator_port;
tlm_utils::peq_with_get<tlm::tlm_generic_payload> m_test_peq;
};
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 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::RecvReqFunc);
}
tlm::tlm_sync_enum RecvReqFunc(tlm::tlm_generic_payload &payload,
tlm::tlm_base_protocol_types::tlm_phase_type& phase, 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_Nb> 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_Nb_1 m_init;
MyTarget m_target;
};
int sc_main(int argc, char **argv)
{
MyTop m_top_module("my_top_module_nb2b");
sc_core::sc_start(20, sc_core::SC_NS);
return 0;
}