OMark Posted July 21, 2020 Report Share Posted July 21, 2020 Hello, I have an example of an initiator with large number of sc_out type of ports (let us say around 400). I need to model a target which should be bound to the different output signals of the initiator and handles their written values internally. To have a more generic and minimal code of such a target I thought that the best way is to have a multiport of type sc_port<sc_signal_inout_if<T>,N>, with number N is the number of outputs that the target should receive. I tried the following code with N=2 as a probe: struct test_producer : sc_module{ sc_out<sc_dt::sc_logic> out1; sc_out<sc_dt::sc_logic> out2; SC_HAS_PROCESS(test_producer); test_producer(::sc_core::sc_module_name){ SC_THREAD(test_prod); dont_initialize(); } void test_prod(){ while (1){ out1.write(SC_LOGIC_0); out2.write(SC_LOGIC_0); wait(10, SC_NS); } } }; struct test_module : sc_module { SC_HAS_PROCESS(test_module); test_module(::sc_core::sc_module_name) { SC_THREAD(test_thread); } sc_port<sc_signal_inout_if<sc_dt::sc_logic>, 2, SC_ZERO_OR_MORE_BOUND> prt_input; void test_thread() { // Create dynamic sensitivity list sc_event_or_list any_port; for (int i = 0; i<prt_input.size(); ++i) { any_port |= prt_input->value_changed_event(); } sc_dt::sc_logic data; int id; for (;;) { wait(any_port); for (int i = 0; i < prt_input.size(); ++i) { if (prt_input->event()) { data = prt_input->read(); id = i; std::cout << std::dec << "From port id " << id << " port[" << i << "] received " << "0x" << std::hex << data << " at " << sc_time_stamp() << std::endl; } } } } }; int sc_main(int argc, char** argv) { test_producer tprod{ "tprod" }; test_module tmod{ "tmod" }; tprod.out1(tmod.prt_input); tprod.out2(tmod.prt_input); sc_start(); return 0; } The code builds successfully, but I got the following error when running it: What I did wrong here and how can I model the interface of such a target efficiently? Shall I insert for instance any kind of converters between the initiator and the target? B.T.W. I thought as well about having and sc_vector type of inputs of my target module. But I am not sure how to bind an sc_vector type of port to each sc_out output port of my initiator and if that is even allowed. Any advise in this direction as well? Thanks in advance for the help. Quote Link to comment Share on other sites More sharing options...
Eyck Posted July 21, 2020 Report Share Posted July 21, 2020 You cannot bind an output port to an input port. Ultimately each port must bind to a signal either hierarchically (this is where port to port binding can is used) or directly. So you need to define signal for each of the test_module output ports and bind the sc_out and sc_inout ports to it. 2 remarks: you should use sc_in instead of sc_inout to indicate the purpose clearly sc_out is just a child of sc_inout to reduce the number of output ports (and hence signal) you might want to group signals logically belonging together into structs. Those can be used as data typs of signals and ports. This reduces the number of signals and events and increases simulation speed. David Black and maehne 2 Quote Link to comment Share on other sites More sharing options...
OMark Posted July 21, 2020 Author Report Share Posted July 21, 2020 Hello, Thanks @Eyck for your prompt response. I corrected the code example as below and the binding works fine now. #include "stdafx.h" #include "systemc.h" struct test_producer : sc_module{ sc_out<sc_dt::sc_logic> out1; sc_out<sc_dt::sc_logic> out2; SC_HAS_PROCESS(test_producer); test_producer(::sc_core::sc_module_name){ SC_THREAD(test_prod); } void test_prod(){ while (1){ out1.write(SC_LOGIC_0); out2.write(SC_LOGIC_0); wait(10, SC_NS); out1.write(SC_LOGIC_1); wait(30, SC_NS); out2.write(SC_LOGIC_1); } } }; struct test_module : sc_module { SC_HAS_PROCESS(test_module); test_module(::sc_core::sc_module_name) { SC_THREAD(test_thread); } sc_port<sc_signal_in_if<sc_dt::sc_logic>, 2, SC_ZERO_OR_MORE_BOUND> prt_input; void test_thread() { // Create dynamic sensitivity list sc_event_or_list any_port; for (int i = 0; i<prt_input.size(); ++i) { any_port |= prt_input->value_changed_event(); } sc_dt::sc_logic data; int id; for (;;) { wait(any_port); for (int i = 0; i < prt_input.size(); ++i) { if (prt_input->event()) { data = prt_input->read(); id = i; std::cout << std::dec << "From port id " << id << " port[" << i << "] received " << "0x" << std::hex << data << " at " << sc_time_stamp() << std::endl; } } } } }; int sc_main(int argc, char** argv) { sc_signal<sc_dt::sc_logic> sig1; sc_signal<sc_dt::sc_logic> sig2; test_producer tprod{ "tprod" }; test_module tmod{ "tmod" }; tprod.out1(sig1); tmod.prt_input(sig1); tprod.out2(sig2); tmod.prt_input(sig2); sc_start(); return 0; } But, I am not sure I understood your second remark "to reduce the number of output ports (and hence signal) you might want to group signals logically belonging together into structs. Those can be used as data typs of signals and ports. This reduces the number of signals and events and increases simulation speed. " I am really interested in having a non-intrusive behaviour of that target module in terms of simulation speed mainly. It would be very helpful if can give an example of such logic groups of ports and signals on target side as you imagine(d) it. In my use case I have the pre-condition, that the initiator module is a 3rd party IP and I cannot restructure it using the logic groups as you proposed. Then, would it even make sense to have logic groups of input ports on target side only? Thanks in advance. Quote Link to comment Share on other sites More sharing options...
Eyck Posted July 21, 2020 Report Share Posted July 21, 2020 My point was more of a generic nature and more related to modeling guidelines. If your initiator/test_producer has many signals some of them might belong 'logically' together as they are written at the same time. E.g in AHB you have HADDR, HTRANS, HBURST, HWRITE, etc. Instead of declaring them as sc_out<sc_dt::sc_logic> xxx or alike: sc_out<sc_dt::sc_lv<32>> HADDR; sc_out<sc_dt::sc_lv<4>> HTRANS; sc_out<sc_dt::sc_logic> HWRITE; . . . it would make sense to do it this way: struct ahb_req { sc_dt::sc_lv<32> HADDR; sc_dt::sc_lv<4> HTRANS; sc_dt::sc_logic HWRITE; . . . }; sc_out<ahb_req> ahb_out; In the first case you have basically 3 signals/events to observe and deal with. In the latter one you only have 1 event. If you cannot refactor the the producer than doing so just for the receiver is point less. But if you have the option to refactor the initiator then I would recommend to do it in this or a similar way. maehne 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.