В документации Doulos SystemC Transfer Level Model написано
Модуль верхнего уровня иерархии создает экземпляр одного инициатора и одной памяти и связывает сокет инициатора в инициаторе с целевым сокетом в целевой памяти. Сокеты инкапсулируют все необходимое для двусторонней связи между модулями, включая порты и экспорты для обоих направлений связи. Один сокет инициатора всегда привязан к одному целевому сокету.
Насколько я понял, когда вы создаете инициатора и цель, инициатор начинает связь, вызывая b_transport, тем самым запуская цель, которая может ответить. Тем не менее, я пишу некоторый код, и это, похоже, не так. Давайте посмотрим на пример.
У меня есть очень простая реализация сумматора, с которой можно поговорить, используя моделирование на уровне передачи. Это серверы модулей в качестве цели.
adder.cc
#define SC_INCLUDE_DYNAMIC_PROCESS
#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/simple_target_socket.h"
#include <iostream>
using namespace sc_core;
using namespace std;
#include "adder.h"
adder::adder(sc_module_name name)
: sc_module(name), socket("socket2")
{
socket.register_b_transport(this, &adder::b_transport);
socket.register_transport_dbg(this, &adder::transport_dbg);
}
void adder::b_transport(tlm::tlm_generic_payload& trans, sc_time& delay)
{
tlm::tlm_command cmd = trans.get_command();
sc_dt::uint64 addr = trans.get_address();
uint32_t *ptr = (uint32_t*)trans.get_data_ptr();
unsigned int len = trans.get_data_length();
unsigned char *byt = trans.get_byte_enable_ptr();
unsigned int wid = trans.get_streaming_width();
addend1 = *ptr;
addend2 = *(++ptr);
add();
cout << "addend1: " << addend1 << endl;
cout << "addend2: " << addend2 << endl;
cout << "sum: " << sum << endl;
uint32_t *return_sum_loc = ptr;
for(int i = 0; i< 2; i++) {
return_sum_loc++;
}
memcpy(return_sum_loc, (char*) &sum, sizeof(uint32_t));
cout << "New sum for return: " << *(return_sum_loc) << endl;
}
unsigned int adder::transport_dbg(tlm::tlm_generic_payload& trans)
{
return 0;
}
void adder::add()
{
sum = addend1 + addend2;
}
Затем у меня есть модуль test_bench, который будет инициатором.
test_bench.cc
#define SC_INCLUDE_DYNAMIC_PROCESS
#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/simple_target_socket.h"
using namespace sc_core;
using namespace std;
#include "test_bench.h"
#include <fstream>
#include <iostream>
test_bench::test_bench(sc_module_name name):
sc_module(name), socket("socket")
{
SC_THREAD(run_tests);
}
void test_bench::run_tests()
{
ifstream infile("./adder.golden.dat");
ofstream ofs;
ofs.open("./adder.dat");
uint32_t theoretical_sum = 0;
while(infile >> data[0] >> data[1] >> theoretical_sum)
{
tlm::tlm_generic_payload *trans = new tlm::tlm_generic_payload;
sc_time delay = sc_time(10, SC_NS);
cout << "Sending" << endl;
cout << "Data[0]: " << data[0] << endl;
cout << "Data[1]: " << data[1] << endl;
trans->set_data_ptr((unsigned char*)data);
socket->b_transport(*trans, delay);
cout << "data[2]" << data[2] << endl;
ofs << data[0] << "\t" << data[1] << "\t" << data[2] << "\n";
delete trans;
}
infile.close();
ofs.close();
printf ("Comparing against output data \n");
if (system("diff -w adder.dat adder.golden.dat"))
{
cout << "*******************************************" << endl;
cout << "FAIL: Output DOES NOT match the golden output" << endl;
cout << "*******************************************" << endl;
}
else
{
cout << "*******************************************" << endl;
cout << "PASS: The output matches the golden output!" << endl;
cout << "*******************************************" << endl;
}
}
Вот родительский модуль, который создает и соединяет их.
main.cc
#include "systemc.h"
#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/simple_target_socket.h"
#include "tlm_utils/tlm_quantumkeeper.h"
using namespace sc_core;
using namespace sc_dt;
using namespace std;
#include "test_bench.h"
#include "adder.h"
SC_MODULE(Top)
{
test_bench *tb;
adder *ad;
sc_signal<bool> rst;
sc_signal<bool> tb_irq;
sc_signal<bool> ad_irq;
Top(sc_module_name name) :
rst("rst")
{
tb = new test_bench("test_bench");
ad = new adder("adder");
tb->socket.bind(ad->socket);
tb->irq(tb_irq);
ad->irq(ad_irq);
}
};
int sc_main(int argc, char *argv[])
{
Top *top = new Top("Top");
sc_start();
}
Когда я запускаю исполняемый файл, это результат, который я получаю.
< 1 0 0
< 1 1 0
< 2 1 0
< 2 2 0
< 2 3 0
< 3 3 0
< 4 3 0
< 4 4 0
< 5 4 0
< 5 5 0
1 0 1
1 1 2
2 1 3
2 2 4
2 3 5
3 3 6
4 3 7
4 4 8
5 4 9
5 5 10
FAIL: Выход НЕ соответствует золотому выходу
Итак, моя первоначальная мысль заключалась в том, что вы передаете по значению эту полезную нагрузку в функцию b_transport инициатора, которая привязана к цели. Цель получит и расшифрует эту полезную нагрузку. Эта часть происходит. Я могу проанализировать uint32_t, переданный по значению, в data[]. Что я в конце концов понял, основываясь на своих 0 возвращаемых значениях, которые были записаны в переданную память, так это то, что это на самом деле не передается по значению. По какой-то причине он создается как тип указателя, затем он разыменовывается при передаче. По сути, это лишает цель возможности манипулировать памятью, которая была передана для передачи ответа инициатору.
Так что вся эта двусторонняя коммуникация, о которой упоминала Эйнсли, меня немного смутила. Под двусторонней связью он подразумевает, что обоим модулям нужны сокеты цели и инициатора, чтобы обеспечить двустороннюю связь?