scc  2024.06
SystemC components library
axi_target_pe.cpp
1 /*
2  * Copyright 2020 Arteris IP
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.axi_util.cpp
15  */
16 
17 #ifndef SC_INCLUDE_DYNAMIC_PROCESSES
18 #define SC_INCLUDE_DYNAMIC_PROCESSES
19 #endif
20 
21 #include <axi/pe/axi_target_pe.h>
22 #include <axi/fsm/protocol_fsm.h>
23 #include <axi/fsm/types.h>
24 #include <scc/report.h>
25 #include <scc/utilities.h>
26 #include <systemc>
27 #include <tuple>
28 
29 using namespace sc_core;
30 using namespace tlm;
31 using namespace axi;
32 using namespace axi::fsm;
33 using namespace axi::pe;
34 
35 /******************************************************************************
36  * target
37  ******************************************************************************/
39  axi_target_pe* const that;
41  : that(that) {}
42  unsigned transport(tlm::tlm_generic_payload& payload) override {
43  if((payload.is_read() && that->rd_resp_fifo.num_free())){
44  that->rd_resp_fifo.write(&payload);
45  return 0;
46  } else if((payload.is_write() && that->wr_resp_fifo.num_free())){
47  that->wr_resp_fifo.write(&payload);
48  return 0;
49  }
50  return std::numeric_limits<unsigned>::max();
51  }
52 };
53 
54 SC_HAS_PROCESS(axi_target_pe);
55 
56 axi_target_pe::axi_target_pe(const sc_core::sc_module_name& nm, size_t transfer_width, flavor_e flavor)
57 : sc_module(nm)
58 , base(transfer_width, (flavor != flavor_e::AXI)) // based on flavor, set the coherent flag of base
59 , bw_intor(new bw_intor_impl(this)) {
60  instance_name = name();
61 
62  bw_i.bind(*bw_intor);
63 
64  SC_METHOD(fsm_clk_method);
65  dont_initialize();
66  sensitive << clk_i.pos();
67  SC_METHOD(process_req2resp_fifos);
68  dont_initialize();
69  sensitive << clk_i.pos();
70  SC_THREAD(start_wr_resp_thread);
71  SC_THREAD(start_rd_resp_thread);
72  SC_THREAD(send_wr_resp_beat_thread);
73  SC_THREAD(send_rd_resp_beat_thread);
74 }
75 
76 axi_target_pe::~axi_target_pe() = default;
77 
78 void axi_target_pe::end_of_elaboration() {
79  clk_if = dynamic_cast<sc_core::sc_clock*>(clk_i.get_interface());
80 }
81 
82 void axi_target_pe::start_of_simulation() {
83  if (!socket_bw)
84  SCCFATAL(SCMOD) << "No backward interface registered!";
85 }
86 
87 void axi_target_pe::b_transport(payload_type& trans, sc_time& t) {
88  auto latency = operation_cb ? operation_cb(trans) : trans.is_read() ? get_cci_randomized_value(rd_resp_delay) : get_cci_randomized_value(wr_resp_delay);
89  trans.set_dmi_allowed(false);
90  trans.set_response_status(tlm::TLM_OK_RESPONSE);
91  if(clk_if)
92  t += clk_if->period() * latency;
93 }
94 
95 tlm_sync_enum axi_target_pe::nb_transport_fw(payload_type& trans, phase_type& phase, sc_time& t) {
96  fw_peq.notify(trans, phase, t);
97  return tlm::TLM_ACCEPTED;
98 }
99 
100 bool axi_target_pe::get_direct_mem_ptr(payload_type& trans, tlm_dmi& dmi_data) {
101  trans.set_dmi_allowed(false);
102  return false;
103 }
104 
105 unsigned int axi_target_pe::transport_dbg(payload_type& trans) { return 0; }
106 
108 
110  fsm_hndl->fsm->cb[RequestPhaseBeg] = [this, fsm_hndl]() -> void {
111  fsm_hndl->beat_count = 0;
112  outstanding_cnt[fsm_hndl->trans->get_command()]++;
113  };
114  fsm_hndl->fsm->cb[BegPartReqE] = [this, fsm_hndl]() -> void {
115  if(!fsm_hndl->beat_count && max_outstanding_tx.get_value() &&
116  outstanding_cnt[fsm_hndl->trans->get_command()] > max_outstanding_tx.get_value()) {
117  stalled_tx[fsm_hndl->trans->get_command()] = fsm_hndl->trans.get();
118  stalled_tp[fsm_hndl->trans->get_command()] = EndPartReqE;
119  } else { // accepted, schedule response
120  if(!fsm_hndl->beat_count)
121  getOutStandingTx(fsm_hndl->trans->get_command())++;
122  if(auto delay = get_cci_randomized_value(wr_data_accept_delay))
123  schedule(EndPartReqE, fsm_hndl->trans, delay - 1);
124  else
125  schedule(EndPartReqE, fsm_hndl->trans, sc_core::SC_ZERO_TIME);
126  }
127  };
128  fsm_hndl->fsm->cb[EndPartReqE] = [this, fsm_hndl]() -> void {
129  tlm::tlm_phase phase = axi::END_PARTIAL_REQ;
130  sc_time t(clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME);
131  auto ret = socket_bw->nb_transport_bw(*fsm_hndl->trans, phase, t);
132  fsm_hndl->beat_count++;
133  };
134  fsm_hndl->fsm->cb[BegReqE] = [this, fsm_hndl]() -> void {
135  if(!fsm_hndl->beat_count && max_outstanding_tx.get_value() &&
136  outstanding_cnt[fsm_hndl->trans->get_command()] > max_outstanding_tx.get_value()) {
137  stalled_tx[fsm_hndl->trans->get_command()] = fsm_hndl->trans.get();
138  stalled_tp[fsm_hndl->trans->get_command()] = EndReqE;
139  } else { // accepted, schedule response
140  if(!fsm_hndl->beat_count)
141  getOutStandingTx(fsm_hndl->trans->get_command())++;
142  auto latency = fsm_hndl->trans->is_read() ? get_cci_randomized_value(rd_addr_accept_delay) : get_cci_randomized_value(wr_data_accept_delay);
143  if(latency)
144  schedule(EndReqE, fsm_hndl->trans, latency - 1);
145  else
146  schedule(EndReqE, fsm_hndl->trans, sc_core::SC_ZERO_TIME);
147  }
148  };
149  fsm_hndl->fsm->cb[EndReqE] = [this, fsm_hndl]() -> void {
150  tlm::tlm_phase phase = tlm::END_REQ;
151  sc_time t(clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME);
152  auto ret = socket_bw->nb_transport_bw(*fsm_hndl->trans, phase, t);
153  fsm_hndl->trans->set_response_status(tlm::TLM_OK_RESPONSE);
154  //it is better to move the set_resp in testcase
155  if(auto ext3 = fsm_hndl->trans->get_extension<axi3_extension>()) {
156  ext3->set_resp(resp_e::OKAY);
157  } else if(auto ext4 = fsm_hndl->trans->get_extension<axi4_extension>()) {
158  ext4->set_resp(resp_e::OKAY);
159  } else if(auto exta = fsm_hndl->trans->get_extension<ace_extension>()) {
160  exta->set_resp(resp_e::OKAY);
161  } else
162  sc_assert(false && "No valid AXITLM extension found!");
163  if(fw_o.get_interface())
164  fw_o->transport(*(fsm_hndl->trans));
165  else {
166  auto latency = operation_cb ? operation_cb(*fsm_hndl->trans)
167  : fsm_hndl->trans->is_read() ? get_cci_randomized_value(rd_resp_delay) : get_cci_randomized_value(wr_resp_delay);
168  if(latency < std::numeric_limits<unsigned>::max()) {
169  if(fsm_hndl->trans->is_write())
170  wr_req2resp_fifo.push_back(std::make_tuple(fsm_hndl->trans.get(), latency));
171  else if(fsm_hndl->trans->is_read())
172  rd_req2resp_fifo.push_back(std::make_tuple(fsm_hndl->trans.get(), latency));
173  }
174  }
175  };
176  fsm_hndl->fsm->cb[BegPartRespE] = [this, fsm_hndl]() -> void {
177  // scheduling the response
178  if(fsm_hndl->trans->is_read()) {
179  if(!rd_resp_beat_fifo.nb_write(std::make_tuple(fsm_hndl, BegPartRespE)))
180  SCCERR(SCMOD) << "too many outstanding transactions";
181  } else if(fsm_hndl->trans->is_write()) {
182  if(!wr_resp_beat_fifo.nb_write(std::make_tuple(fsm_hndl, BegPartRespE)))
183  SCCERR(SCMOD) << "too many outstanding transactions";
184  }
185  };
186  fsm_hndl->fsm->cb[EndPartRespE] = [this, fsm_hndl]() -> void {
187  fsm_hndl->trans->is_read() ? rd_resp_ch.post() : wr_resp_ch.post();
188  auto size = get_burst_length(*fsm_hndl->trans) - 1;
189  fsm_hndl->beat_count++;
190  SCCTRACE(SCMOD)<< " in EndPartialResp with beat_count = " << fsm_hndl->beat_count << " expected size = " << size;
191  if(rd_data_beat_delay.get_value())
192  schedule(fsm_hndl->beat_count < size ? BegPartRespE : BegRespE, fsm_hndl->trans, get_cci_randomized_value(rd_data_beat_delay));
193  else
194  schedule(fsm_hndl->beat_count < size ? BegPartRespE : BegRespE, fsm_hndl->trans, SC_ZERO_TIME, true);
195  };
196  fsm_hndl->fsm->cb[BegRespE] = [this, fsm_hndl]() -> void {
197  // scheduling the response
198  if(fsm_hndl->trans->is_read()) {
199  if(!rd_resp_beat_fifo.nb_write(std::make_tuple(fsm_hndl, BegRespE)))
200  SCCERR(SCMOD) << "too many outstanding transactions";
201  } else if(fsm_hndl->trans->is_write()) {
202  if(!wr_resp_beat_fifo.nb_write(std::make_tuple(fsm_hndl, BegRespE)))
203  SCCERR(SCMOD) << "too many outstanding transactions";
204  }
205  };
206  fsm_hndl->fsm->cb[EndRespE] = [this, fsm_hndl]() -> void {
207  fsm_hndl->trans->is_read() ? rd_resp_ch.post() : wr_resp_ch.post();
208  if(rd_resp.get_value() < rd_resp.get_capacity()) {
209  SCCTRACE(SCMOD) << "finishing exclusive read response for trans " << *fsm_hndl->trans;
210  rd_resp.post();
211  }
212  auto cmd = fsm_hndl->trans->get_command();
213  outstanding_cnt[cmd]--;
214  getOutStandingTx(cmd)--;
215  if(cmd == tlm::TLM_READ_COMMAND)
216  active_rdresp_id.erase(axi::get_axi_id(fsm_hndl->trans.get()));
217  if(stalled_tx[cmd]) {
218  auto* trans = stalled_tx[cmd];
219  auto latency = trans->is_read() ? get_cci_randomized_value(rd_addr_accept_delay) : get_cci_randomized_value(wr_data_accept_delay);
220  if(latency)
221  schedule(stalled_tp[cmd], trans, latency - 1);
222  else
223  schedule(stalled_tp[cmd], trans, sc_core::SC_ZERO_TIME);
224  stalled_tx[cmd] = nullptr;
225  stalled_tp[cmd] = CB_CNT;
226  }
227  };
228 }
229 
230 void axi::pe::axi_target_pe::operation_resp(payload_type& trans, unsigned clk_delay) {
231  if(trans.is_write())
232  wr_req2resp_fifo.push_back(std::make_tuple(&trans, clk_delay));
233  else if(trans.is_read())
234  rd_req2resp_fifo.push_back(std::make_tuple(&trans, clk_delay));
235 }
236 
237 void axi::pe::axi_target_pe::process_req2resp_fifos() {
238  while(!rd_req2resp_fifo.empty()) {
239  auto& entry = rd_req2resp_fifo.front();
240  if(std::get<1>(entry) == 0) {
241  if(!rd_resp_fifo.nb_write(std::get<0>(entry)))
242  rd_req2resp_fifo.push_back(entry);
243  rd_req2resp_fifo.pop_front();
244  } else {
245  std::get<1>(entry) -= 1;
246  rd_req2resp_fifo.push_back(entry);
247  rd_req2resp_fifo.pop_front();
248  }
249  }
250  while(!wr_req2resp_fifo.empty()) {
251  auto& entry = wr_req2resp_fifo.front();
252  if(std::get<1>(entry) == 0) {
253  if(!wr_resp_fifo.nb_write(std::get<0>(entry)))
254  wr_req2resp_fifo.push_back(entry);
255  wr_req2resp_fifo.pop_front();
256  } else {
257  std::get<1>(entry) -= 1;
258  wr_req2resp_fifo.push_back(entry);
259  wr_req2resp_fifo.pop_front();
260  }
261  }
262 }
263 
264 void axi::pe::axi_target_pe::start_rd_resp_thread() {
265  auto residual_clocks = 0.0;
266  while(true) {
267  auto* trans = rd_resp_fifo.read();
268  if(!rd_data_interleaving.get_value() || rd_data_beat_delay.get_value() == 0) {
269  while(!rd_resp.get_value())
270  wait(clk_i.posedge_event());
271  rd_resp.wait();
272  }
273  SCCTRACE(SCMOD) << __FUNCTION__ << " starting exclusive read response for trans " << *trans;
274  auto e = axi::get_burst_length(trans) == 1 || trans->is_write() ? axi::fsm::BegRespE : BegPartRespE;
275  auto id = axi::get_axi_id(trans);
276  while(active_rdresp_id.size() && active_rdresp_id.find(id) != active_rdresp_id.end()) {
277  wait(clk_i.posedge_event());
278  }
279  active_rdresp_id.insert(id);
280  if(auto delay = get_cci_randomized_value(rd_data_beat_delay))
281  schedule(e, trans, delay-1U);
282  else
283  schedule(e, trans, SC_ZERO_TIME);
284  }
285 }
286 
287 void axi::pe::axi_target_pe::start_wr_resp_thread() {
288  auto residual_clocks = 0.0;
289  while(true) {
290  auto* trans = wr_resp_fifo.read();
291  schedule(axi::fsm::BegRespE, trans, SC_ZERO_TIME);
292  }
293 }
294 
295 void axi::pe::axi_target_pe::send_rd_resp_beat_thread() {
296  std::tuple<fsm::fsm_handle*, axi::fsm::protocol_time_point_e> entry;
297  while(true) {
298  // waiting for responses to send, which is notifed in Begin_Partial_Resp
299  wait(rd_resp_beat_fifo.data_written_event());
300  while(rd_resp_beat_fifo.nb_read(entry)) {
301  // there is something to send
302  auto fsm_hndl = std::get<0>(entry);
303  auto tp = std::get<1>(entry);
304  sc_time t;
305  tlm::tlm_phase phase{axi::BEGIN_PARTIAL_RESP};
306  if(tp == BegRespE)
307  phase= tlm::BEGIN_RESP;
308  // wait to get ownership of the response channel
309  while(!rd_resp_ch.get_value())
310  wait(clk_i.posedge_event());
311  rd_resp_ch.wait();
312  SCCTRACE(SCMOD) << __FUNCTION__ << " starting exclusive read response for trans " << *fsm_hndl->trans;
313  if(socket_bw->nb_transport_bw(*fsm_hndl->trans, phase, t) == tlm::TLM_UPDATED) {
314  schedule(phase == tlm::END_RESP ? EndRespE : EndPartRespE, fsm_hndl->trans, 0);
315  }
316  }
317  }
318 }
319 
320 void axi::pe::axi_target_pe::send_wr_resp_beat_thread() {
321  std::tuple<fsm::fsm_handle*, axi::fsm::protocol_time_point_e> entry;
322  while(true) {
323  // waiting for responses to send
324  wait(wr_resp_beat_fifo.data_written_event());
325  while(wr_resp_beat_fifo.nb_read(entry)) {
326  // there is something to send
327  auto fsm_hndl = std::get<0>(entry);
328  sc_time t;
329  tlm::tlm_phase phase{tlm::tlm_phase(tlm::BEGIN_RESP)};
330  // wait to get ownership of the response channel
331  wr_resp_ch.wait();
332  if(socket_bw->nb_transport_bw(*fsm_hndl->trans, phase, t) == tlm::TLM_UPDATED) {
333  schedule(phase == tlm::END_RESP ? EndRespE : EndPartRespE, fsm_hndl->trans, 0);
334  }
335  }
336  }
337 }
cci::cci_param< int > rd_addr_accept_delay
the latency between between BEGIN_REQ and END_REQ (ARVALID to ARREADY) -> APR
Definition: axi_target_pe.h:78
cci::cci_param< int > wr_data_accept_delay
the latency between between BEGIN(_PARTIAL)_REQ and END(_PARTIAL)_REQ (AWVALID to AWREADY and WVALID ...
Definition: axi_target_pe.h:74
fsm::fsm_handle * create_fsm_handle() override
void operation_resp(payload_type &trans, unsigned clk_delay=0)
cci::cci_param< int > rd_data_beat_delay
the latency between between END(_PARTIAL)_RESP and BEGIN(_PARTIAL)_RESP (RREADY to RVALID) -> RBV
Definition: axi_target_pe.h:82
void setup_callbacks(fsm::fsm_handle *) override
cci::cci_param< int > rd_resp_delay
the latency between request and response phase. Will be overwritten by the return of the callback fun...
Definition: axi_target_pe.h:87
cci::cci_param< unsigned > max_outstanding_tx
the number of supported outstanding transactions. If this limit is reached the target starts to do ba...
Definition: axi_target_pe.h:65
cci::cci_param< int > wr_resp_delay
the latency between request and response phase. Will be overwritten by the return of the callback fun...
Definition: axi_target_pe.h:92
int get_value() const override
get the value of the semaphore
unsigned get_capacity()
retrieve the initial capacity of the semaphore
int post() override
unlock (give) the semaphore
T * get() const noexcept
Return the stored pointer.
Definition: tlm_gp_shared.h:91
protocol engine implementations
Definition: ace_target_pe.h:37
TLM2.0 components modeling AHB.
Definition: axi_initiator.h:30
unsigned get_burst_length(const request &r)
Definition: axi_tlm.h:1167
SystemC TLM.
Definition: cxs_tlm.h:69
base class of all AXITLM based adapters and interfaces.
Definition: base.h:43
void schedule(axi::fsm::protocol_time_point_e e, tlm::scc::tlm_gp_shared_ptr &gp, unsigned cycles)
processes the fsm_sched_queue and propagates events to fsm_clk_queue. Should be registered as falling...
Definition: base.h:107
axi::axi_protocol_types::tlm_payload_type payload_type
aliases used in the class
Definition: base.h:45
tlm::scc::tlm_gp_shared_ptr trans
pointer to the associated AXITLM payload
Definition: types.h:62
size_t beat_count
beat count of this transaction
Definition: types.h:64
AxiProtocolFsm *const fsm
pointer to the FSM
Definition: types.h:60
unsigned transport(tlm::tlm_generic_payload &payload) override