scc  2022.4.0
SystemC components library
simple_initiator.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 #include "simple_initiator.h"
18 #include <axi/fsm/protocol_fsm.h>
19 #include <axi/fsm/types.h>
20 #include <scc/report.h>
21 #include <systemc>
22 #include <tlm/scc/tlm_id.h>
23 
24 using namespace sc_core;
25 using namespace tlm;
26 using namespace axi;
27 using namespace axi::fsm;
28 using namespace axi::pe;
29 
30 /******************************************************************************
31  * initiator
32  ******************************************************************************/
33 SC_HAS_PROCESS(simple_initiator_b);
34 
35 simple_initiator_b::simple_initiator_b(const sc_core::sc_module_name& nm,
36  sc_core::sc_port_b<axi::axi_fw_transport_if<axi_protocol_types>>& port,
37  size_t transfer_width, bool ack_phase)
38 : sc_module(nm)
39 , base(transfer_width, ack_phase)
40 , socket_fw(port) {
41  add_attribute(wr_data_beat_delay);
42  add_attribute(rd_data_accept_delay);
43  add_attribute(wr_resp_accept_delay);
44  add_attribute(ack_resp_delay);
45  fw_i.bind(*this);
46  SC_METHOD(fsm_clk_method);
47  dont_initialize();
48  sensitive << clk_i.pos();
49  SC_METHOD(process_snoop_resp);
50  sensitive << clk_i.pos();
51  snp_resp_queue.set_avail_cb([this]() {
52  if(snp_resp_queue_hndl.valid())
53  snp_resp_queue_hndl.enable();
54  });
55  snp_resp_queue.set_empty_cb([this]() {
56  if(snp_resp_queue_hndl.valid())
57  snp_resp_queue_hndl.disable();
58  });
59  SC_METHOD(cbpeq_cb);
60  dont_initialize();
61  sensitive << cbpeq.event();
62 }
63 
64 void simple_initiator_b::end_of_elaboration() { clk_if = dynamic_cast<sc_core::sc_clock*>(clk_i.get_interface()); }
65 
66 // bool simple_initiator_b::operation(bool write, uint64_t addr, unsigned len, const uint8_t* data, bool blocking) {
67 void simple_initiator_b::transport(payload_type& trans, bool blocking) {
68  // auto ext = trans.get_extension<axi::axi4_extension>();
69  // sc_assert(ext!=nullptr);
70  SCCTRACE(SCMOD) << "got transport req for trans " << trans;
71  if(blocking) {
72  sc_time t;
73  socket_fw->b_transport(trans, t);
74  } else {
75  fsm_handle* fsm = find_or_create(&trans);
76  if(trans.is_read()) {
77  rd.wait();
78  wait(clk_i.posedge_event());
79  } else {
80  wr.wait();
81  wait(clk_i.posedge_event());
82  }
83  react(RequestPhaseBeg, fsm);
84  SCCTRACE(SCMOD) << "started non-blocking protocol";
85  sc_core::wait(fsm->finish);
86  SCCTRACE(SCMOD) << "finished non-blocking protocol";
87  }
88 }
89 
91 
93  fsm_hndl->fsm->cb[RequestPhaseBeg] = [this, fsm_hndl]() -> void {
94  fsm_hndl->beat_count = 0;
95  auto& f = protocol_cb[RequestPhaseBeg];
96  if(f)
97  f(*fsm_hndl->trans, fsm_hndl->is_snoop);
98  };
99  fsm_hndl->fsm->cb[BegPartReqE] = [this, fsm_hndl]() -> void {
100  sc_time t;
101  tlm::tlm_phase phase = axi::BEGIN_PARTIAL_REQ;
102  auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
103  if(ret == tlm::TLM_UPDATED) {
104  schedule(EndPartReqE, fsm_hndl->trans, t, true);
105  }
106  if(protocol_cb[BegPartReqE])
107  cbpeq.notify(std::make_tuple(BegPartReqE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
108  };
109  fsm_hndl->fsm->cb[EndPartReqE] = [this, fsm_hndl]() -> void {
110  fsm_hndl->beat_count++;
111  if(fsm_hndl->beat_count < (get_burst_length(*fsm_hndl->trans) - 1))
112  if(::scc::get_value(wr_data_beat_delay) > 0)
113  schedule(BegPartReqE, fsm_hndl->trans, ::scc::get_value(wr_data_beat_delay) - 1);
114  else
115  schedule(BegPartReqE, fsm_hndl->trans, SC_ZERO_TIME);
116  else if(::scc::get_value(wr_data_beat_delay) > 0)
117  schedule(BegReqE, fsm_hndl->trans, ::scc::get_value(wr_data_beat_delay) - 1);
118  else
119  schedule(BegReqE, fsm_hndl->trans, 0);
120  if(protocol_cb[EndPartReqE])
121  cbpeq.notify(std::make_tuple(EndPartReqE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
122  };
123  fsm_hndl->fsm->cb[BegReqE] = [this, fsm_hndl]() -> void {
124  if(fsm_hndl->is_snoop) {
125  schedule(EndReqE, fsm_hndl->trans, SC_ZERO_TIME);
126  } else {
127  sc_time t;
128  tlm::tlm_phase phase = tlm::BEGIN_REQ;
129  auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
130  if(ret == tlm::TLM_UPDATED) {
131  schedule(EndReqE, fsm_hndl->trans, t, true);
132  }
133  }
134  if(protocol_cb[BegReqE])
135  cbpeq.notify(std::make_tuple(BegReqE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
136  };
137  fsm_hndl->fsm->cb[EndReqE] = [this, fsm_hndl]() -> void {
138  if(fsm_hndl->is_snoop) {
139  tlm::tlm_phase phase = tlm::END_REQ;
140  sc_time t(clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME);
141  auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
142  auto ext = fsm_hndl->trans->get_extension<ace_extension>();
143  sc_assert(ext && "No ACE extension found for snoop access");
144  fsm_hndl->beat_count = 0;
145  fsm_hndl->trans->set_response_status(tlm::TLM_OK_RESPONSE);
146  auto latency = snoop_latency;
147  if(bw_o.get_interface()) {
148  // since the bw_o interface is blocking we need to use threads to check for the latency in the bw path
149  if(thread_avail == 0) {
150  sc_core::sc_spawn_options opts;
151  opts.set_stack_size(0x10000);
152  sc_core::sc_spawn(
153  [this]() {
154  thread_active++;
155  while(true) {
156  thread_avail++;
157  auto req = dispatch_queue.read();
158  sc_assert(thread_avail > 0);
159  thread_avail--;
160  auto latency = bw_o->transport(*req);
161  if(latency < std::numeric_limits<unsigned>::max()) {
162  auto ext = req->get_extension<ace_extension>();
163  auto length = ext->get_length() + 1;
164  auto evt = ext->is_snoop_data_transfer() && length > 1 ? BegPartRespE : BegRespE;
165  snp_resp_queue.push_back(std::make_tuple(evt, req.get(), latency));
166  }
167  }
168  },
169  nullptr, &opts);
170  }
171  dispatch_queue.write(fsm_hndl->trans);
172  latency = std::numeric_limits<unsigned>::max();
173  } else if(snoop_cb) {
174  latency = snoop_cb(*fsm_hndl->trans);
175  } else {
176  ext->set_snoop_data_transfer(false);
177  ext->set_snoop_error(false);
178  ext->set_pass_dirty(false);
179  ext->set_shared(false);
180  ext->set_snoop_was_unique(false);
181  }
182  if(latency < std::numeric_limits<unsigned>::max()) {
183  auto length = axi::get_burst_length(*fsm_hndl->trans);
184  auto evt = ext->is_snoop_data_transfer() && length > 1 ? BegPartRespE : BegRespE;
185  snp_resp_queue.push_back(std::make_tuple(evt, fsm_hndl->trans.get(), latency));
186  }
187  } else {
188  // auto ext = fsm_hndl->trans->get_extension<axi::axi4_extension>();
189  if(fsm_hndl->trans->is_write())
190  wr.post();
191  else
192  rd.post();
193  }
194  if(protocol_cb[EndReqE])
195  cbpeq.notify(std::make_tuple(EndReqE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
196  };
197  fsm_hndl->fsm->cb[BegPartRespE] = [this, fsm_hndl]() -> void {
198  if(fsm_hndl->is_snoop) {
199  tlm::tlm_phase phase = axi::BEGIN_PARTIAL_RESP;
200  sc_time t;
201  auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
202  } else {
203  if(::scc::get_value(rd_data_accept_delay))
204  schedule(EndPartRespE, fsm_hndl->trans, ::scc::get_value(rd_data_accept_delay));
205  else
206  schedule(EndPartRespE, fsm_hndl->trans, SC_ZERO_TIME);
207  }
208  if(protocol_cb[BegPartRespE])
209  cbpeq.notify(std::make_tuple(BegPartRespE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
210  };
211  fsm_hndl->fsm->cb[EndPartRespE] = [this, fsm_hndl]() -> void {
212  if(fsm_hndl->is_snoop) {
213  auto size = axi::get_burst_length(*fsm_hndl->trans);
214  fsm_hndl->beat_count++;
215  schedule(fsm_hndl->beat_count < (size-1) ? BegPartRespE : BegRespE, fsm_hndl->trans, 0);
216  } else {
217  sc_time t(clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME);
218  tlm::tlm_phase phase = axi::END_PARTIAL_RESP;
219  auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
220  fsm_hndl->beat_count++;
221  }
222  if(protocol_cb[EndPartRespE])
223  cbpeq.notify(std::make_tuple(EndPartRespE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
224  };
225  fsm_hndl->fsm->cb[BegRespE] = [this, fsm_hndl]() -> void {
226  if(fsm_hndl->is_snoop) {
227  tlm::tlm_phase phase = tlm::BEGIN_RESP;
228  sc_time t;
229  auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
230  } else {
231  auto del = fsm_hndl->trans->is_read() ? ::scc::get_value(rd_data_accept_delay)
232  : ::scc::get_value(wr_resp_accept_delay);
233  if(del)
234  schedule(EndRespE, fsm_hndl->trans, del);
235  else
236  schedule(EndRespE, fsm_hndl->trans, SC_ZERO_TIME);
237  }
238  if(protocol_cb[BegRespE])
239  cbpeq.notify(std::make_tuple(BegRespE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
240  };
241  fsm_hndl->fsm->cb[EndRespE] = [this, fsm_hndl]() -> void {
242  if(fsm_hndl->is_snoop) {
243  snp.post();
244  } else {
245  sc_time t(clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME);
246  tlm::tlm_phase phase = tlm::END_RESP;
247  auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
248  if(coherent) {
249  schedule(Ack, fsm_hndl->trans, ::scc::get_value(ack_resp_delay));
250  } else
251  fsm_hndl->finish.notify(sc_core::SC_ZERO_TIME);
252  }
253  if(protocol_cb[EndRespE])
254  cbpeq.notify(std::make_tuple(EndRespE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
255  };
256  fsm_hndl->fsm->cb[Ack] = [this, fsm_hndl]() -> void {
257  sc_time t;
258  tlm::tlm_phase phase = axi::ACK;
259  auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
260  fsm_hndl->finish.notify(sc_core::SC_ZERO_TIME);
261  if(protocol_cb[Ack])
262  cbpeq.notify(std::make_tuple(Ack, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
263  };
264 }
265 
266 tlm_sync_enum simple_initiator_b::nb_transport_bw(payload_type& trans, phase_type& phase, sc_time& t) {
267  auto ret = TLM_ACCEPTED;
268  SCCTRACE(SCMOD) << "nb_transport_bw " << phase << " of trans " << trans;
269  if(phase == END_PARTIAL_REQ || phase == END_REQ) { // read/write
270  schedule(phase == END_REQ ? EndReqE : EndPartReqE, &trans, t, false);
271  } else if(phase == BEGIN_PARTIAL_RESP || phase == BEGIN_RESP) { // read/write response
272  schedule(phase == BEGIN_RESP ? BegRespE : BegPartRespE, &trans, t, false);
273  } else if(phase == BEGIN_REQ) { // snoop read
274  auto fsm_hndl = find_or_create(&trans, true);
275  fsm_hndl->is_snoop = true;
276  schedule(BegReqE, &trans, t);
277  } else if(phase == END_PARTIAL_RESP || phase == END_RESP) { // snoop read response
278  schedule(phase == END_RESP ? EndRespE : EndPartRespE, &trans, t);
279  }
280  return ret;
281 }
282 
283 void simple_initiator_b::b_snoop(payload_type& trans, sc_time& t) {
284  if(bw_o.get_interface()) {
285  auto latency = bw_o->transport(trans);
286  if(latency < std::numeric_limits<unsigned>::max())
287  t += latency * clk_period;
288  } else if(snoop_cb) {
289  auto latency = snoop_cb(trans);
290  if(latency < std::numeric_limits<unsigned>::max())
291  t += latency * clk_period;
292  }
293 }
294 
295 void simple_initiator_b::process_snoop_resp() {
296  if(!snp_resp_queue_hndl.valid())
297  snp_resp_queue_hndl = sc_process_handle(sc_get_current_process_handle());
298  if(snp_resp_queue.avail())
299  while(snp_resp_queue.avail()) {
300  auto entry = snp_resp_queue.front();
301  if(std::get<2>(entry) == 0) {
302  if(snp.get_value() == 0)
303  snp_resp_queue.push_back(entry);
304  else {
305  snp.wait();
306  auto gp = std::get<1>(entry);
307  SCCTRACE(instance_name)
308  << "processing event " << evt2str(std::get<0>(entry)) << " of trans " << *gp;
309  react(std::get<0>(entry), std::get<1>(entry));
310  }
311  } else {
312  std::get<2>(entry) -= 1;
313  snp_resp_queue.push_back(entry);
314  }
315  snp_resp_queue.pop_front();
316  }
317  else
318  // fall asleep if there is nothing to process
319  snp_resp_queue_hndl.disable();
320 }
321 
323  axi::fsm::fsm_handle* fsm_hndl = active_fsm[&trans];
324  sc_assert(fsm_hndl != nullptr);
325  auto ext = fsm_hndl->trans->get_extension<ace_extension>();
326  auto size = ext->get_length();
327  protocol_time_point_e e = ext->is_snoop_data_transfer() && size > 0 ? BegPartRespE : BegRespE;
328  if(snp.get_value() == 0) {
329  snp_resp_queue.push_back(std::make_tuple(e, &trans, 0));
330  } else {
331  snp.wait();
332  if(sync)
333  schedule(e, fsm_hndl->trans, 0);
334  else
335  react(e, fsm_hndl->trans);
336  }
337 }
338 
339 void simple_initiator_b::cbpeq_cb() {
340  while(cbpeq.has_next()) {
341  auto e = cbpeq.get();
342  protocol_cb[std::get<0>(e)](*std::get<1>(e), std::get<2>(e));
343  }
344 }
void setup_callbacks(axi::fsm::fsm_handle *) override
axi::fsm::fsm_handle * create_fsm_handle() override
void snoop_resp(payload_type &trans, bool sync=false) override
triggers a non-blocking snoop response if the snoop callback does not do so.
sc_core::sc_attribute< unsigned > rd_data_accept_delay
the latency between between BEGIN(_PARTIAL)_RESP and END(_PARTIAL)_RESP (RVALID to RREADY)
sc_core::sc_attribute< unsigned > wr_resp_accept_delay
the latency between between BEGIN_RESP and END_RESP (BVALID to BREADY)
sc_core::sc_attribute< unsigned > ack_resp_delay
the latency between between BEGIN_RESP and END_RESP (BVALID to BREADY)
sc_core::sc_attribute< unsigned > wr_data_beat_delay
the latency between between END(_PARTIAL)_REQ and BEGIN(_PARTIAL)_REQ (AWREADY to AWVALID and WREADY ...
void transport(payload_type &trans, bool blocking) override
The forward transport function. It behaves blocking and is re-entrant.
int get_value() const override
get the value of the semaphore
int wait() override
lock (take) the semaphore, block if not available
tlm::tlm_generic_payload * 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:1122
tlm::tlm_fw_transport_if< TYPES > axi_fw_transport_if
alias declaration for the forward interface
Definition: axi_tlm.h:916
SystemC TLM.
base class of all AXITLM based adapters and interfaces.
Definition: base.h:43
void react(axi::fsm::protocol_time_point_e event, tlm::scc::tlm_gp_shared_ptr &trans)
triggers the FSM with event and given transaction
Definition: base.h:134
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::fsm::fsm_handle * find_or_create(payload_type *gp=nullptr, bool ace=false)
retrieve the FSM handle based on the transaction passed. If non exist one will be created
Definition: base.cpp:65
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
sc_core::sc_event finish
event indicating the end of the transaction
Definition: types.h:68
size_t beat_count
beat count of this transaction
Definition: types.h:64
AxiProtocolFsm *const fsm
pointer to the FSM
Definition: types.h:60
bool is_snoop
indicator if this is a snoop access
Definition: types.h:66
uint8_t get_length() const
get the AxLEN value of the transaction, the value denotes the burst length - 1
Definition: axi_tlm.h:1382
bool has_next()
check if value is available at current time
Definition: peq.h:156
TYPE get()
blocking get
Definition: peq.h:128
sc_core::sc_event & event()
get the available event
Definition: peq.h:140