scc  2022.4.0
SystemC components library
axi4_initiator.h
1 /*******************************************************************************
2  * Copyright 2021-2022 MINRES Technologies GmbH
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.
15  *******************************************************************************/
16 
17 #ifndef _BUS_AXI_PIN_AXI4_INITIATOR_H_
18 #define _BUS_AXI_PIN_AXI4_INITIATOR_H_
19 
20 #include <axi/axi_tlm.h>
21 #include <axi/fsm/base.h>
22 #include <axi/fsm/protocol_fsm.h>
23 #include <axi/signal_if.h>
24 #include <systemc>
25 #include <tlm_utils/peq_with_cb_and_phase.h>
26 
28 namespace axi {
30 namespace pin {
31 
32 using namespace axi::fsm;
33 
34 template <typename CFG>
35 struct axi4_initiator : public sc_core::sc_module,
36  public aw_axi<CFG, typename CFG::master_types>,
37  public wdata_axi<CFG, typename CFG::master_types>,
38  public b_axi<CFG, typename CFG::master_types>,
39  public ar_axi<CFG, typename CFG::master_types>,
40  public rresp_axi<CFG, typename CFG::master_types>,
41  protected axi::fsm::base,
42  public axi::axi_fw_transport_if<axi::axi_protocol_types> {
43  SC_HAS_PROCESS(axi4_initiator);
44 
45  using payload_type = axi::axi_protocol_types::tlm_payload_type;
46  using phase_type = axi::axi_protocol_types::tlm_phase_type;
47 
48  sc_core::sc_in<bool> clk_i{"clk_i"};
49 
51 
52  axi4_initiator(sc_core::sc_module_name const& nm)
53  : sc_core::sc_module(nm)
54  , base(CFG::BUSWIDTH) {
55  instance_name = name();
56  tsckt(*this);
57  SC_METHOD(clk_delay);
58  sensitive << clk_i.pos();
59  SC_THREAD(ar_t);
60  SC_THREAD(r_t);
61  SC_THREAD(aw_t);
62  SC_THREAD(wdata_t);
63  SC_THREAD(b_t);
64  }
65 
66 private:
67  void b_transport(payload_type& trans, sc_core::sc_time& t) override {
68  trans.set_dmi_allowed(false);
69  trans.set_response_status(tlm::TLM_OK_RESPONSE);
70  }
71 
72  tlm::tlm_sync_enum nb_transport_fw(payload_type& trans, phase_type& phase, sc_core::sc_time& t) override {
73  assert(trans.get_extension<axi::axi4_extension>() && "missing AXI4 extension");
74  sc_core::sc_time delay; // FIXME: calculate delay correctly
75  fw_peq.notify(trans, phase, delay);
76  return tlm::TLM_ACCEPTED;
77  }
78 
79  bool get_direct_mem_ptr(payload_type& trans, tlm::tlm_dmi& dmi_data) override {
80  trans.set_dmi_allowed(false);
81  return false;
82  }
83 
84  unsigned int transport_dbg(payload_type& trans) override { return 0; }
85 
86  void end_of_elaboration() override { clk_if = dynamic_cast<sc_core::sc_clock*>(clk_i.get_interface()); }
87 
88  fsm_handle* create_fsm_handle() { return new fsm_handle(); }
89 
90  void setup_callbacks(fsm_handle* fsm_hndl);
91 
92  void clk_delay() { clk_delayed.notify(axi::CLK_DELAY); }
93 
94  void ar_t();
95  void r_t();
96  void aw_t();
97  void wdata_t();
98  void b_t();
99  std::array<unsigned, 3> outstanding_cnt{0, 0, 0};
100  std::array<fsm_handle*, 3> active_req{nullptr, nullptr, nullptr};
101  std::array<fsm_handle*, 3> active_resp{nullptr, nullptr, nullptr};
102  sc_core::sc_clock* clk_if{nullptr};
103  sc_core::sc_event clk_delayed, clk_self, r_end_resp_evt, aw_evt, ar_evt;
104  void nb_fw(payload_type& trans, const phase_type& phase) {
105  auto delay = sc_core::SC_ZERO_TIME;
106  base::nb_fw(trans, phase, delay);
107  }
108  tlm_utils::peq_with_cb_and_phase<axi4_initiator> fw_peq{this, &axi4_initiator::nb_fw};
109  std::unordered_map<unsigned, std::deque<fsm_handle*>> rd_resp_by_id, wr_resp_by_id;
110  sc_core::sc_buffer<uint8_t> wdata_vl;
111  void write_ar(tlm::tlm_generic_payload& trans);
112  void write_aw(tlm::tlm_generic_payload& trans);
113  void write_wdata(tlm::tlm_generic_payload& trans, unsigned beat, bool last = false);
114 };
115 
116 } // namespace pin
117 } // namespace axi
118 
119 template <typename CFG> inline void axi::pin::axi4_initiator<CFG>::write_ar(tlm::tlm_generic_payload& trans) {
120  sc_dt::sc_uint<CFG::ADDRWIDTH> addr = trans.get_address();
121  this->ar_addr.write(addr);
122  if(auto ext = trans.get_extension<axi::axi4_extension>()) {
123  this->ar_prot.write(ext->get_prot());
124  if(!CFG::IS_LITE) {
125  auto id = ext->get_id();
126  if(id >= (1 << CFG::IDWIDTH))
127  SCCERR(SCMOD) << "ARID value larger that signal arid with width=" << CFG::IDWIDTH << " can carry";
128  this->ar_id->write(sc_dt::sc_uint<CFG::IDWIDTH>(id));
129  this->ar_len->write(sc_dt::sc_uint<8>(ext->get_length()));
130  this->ar_size->write(sc_dt::sc_uint<3>(ext->get_size()));
131  this->ar_burst->write(sc_dt::sc_uint<2>(axi::to_int(ext->get_burst())));
132  if(ext->is_exclusive())
133  this->ar_lock->write(true);
134  this->ar_cache->write(sc_dt::sc_uint<4>(ext->get_cache()));
135  this->ar_qos->write(ext->get_qos());
136  if(this->ar_user.get_interface())
137  this->ar_user->write(ext->get_user(axi::common::id_type::CTRL));
138  }
139  }
140 }
141 
142 template <typename CFG> inline void axi::pin::axi4_initiator<CFG>::write_aw(tlm::tlm_generic_payload& trans) {
143  sc_dt::sc_uint<CFG::ADDRWIDTH> addr = trans.get_address();
144  this->aw_addr.write(addr);
145  if(auto ext = trans.get_extension<axi::axi4_extension>()) {
146  this->aw_prot.write(ext->get_prot());
147  if(!CFG::IS_LITE) {
148  auto id = ext->get_id();
149  if(id >= (1 << CFG::IDWIDTH))
150  SCCERR(SCMOD) << "AWID value larger than signal awid with width=" << CFG::IDWIDTH << " can carry";
151  this->aw_id->write(sc_dt::sc_uint<CFG::IDWIDTH>(id));
152  this->aw_len->write(sc_dt::sc_uint<8>(ext->get_length()));
153  this->aw_size->write(sc_dt::sc_uint<3>(ext->get_size()));
154  this->aw_burst->write(sc_dt::sc_uint<2>(axi::to_int(ext->get_burst())));
155  this->aw_cache->write(sc_dt::sc_uint<4>(ext->get_cache()));
156  this->aw_qos->write(ext->get_qos());
157  if(ext->is_exclusive())
158  this->aw_lock->write(true);
159  if(this->aw_user.get_interface())
160  this->aw_user->write(ext->get_user(axi::common::id_type::CTRL));
161  }
162  }
163 }
164 
165 // FIXME: strb not yet correct
166 template <typename CFG> inline void axi::pin::axi4_initiator<CFG>::write_wdata(tlm::tlm_generic_payload& trans, unsigned beat, bool last) {
167  typename CFG::data_t data{0};
168  sc_dt::sc_uint<CFG::BUSWIDTH / 8> strb{0};
169  auto ext = trans.get_extension<axi::axi4_extension>();
170  auto size = 1u << ext->get_size();
171  auto byte_offset = beat * size;
172  auto offset = (trans.get_address() + byte_offset) & (CFG::BUSWIDTH / 8 - 1);
173  auto beptr = trans.get_byte_enable_length() ? trans.get_byte_enable_ptr() + byte_offset : nullptr;
174  if(offset && (size + offset) > (CFG::BUSWIDTH / 8)) { // un-aligned multi-beat access
175  if(beat == 0) {
176  auto dptr = trans.get_data_ptr();
177  for(size_t i = offset; i < size; ++i, ++dptr) {
178  auto bit_offs = i * 8;
179  data(bit_offs + 7, bit_offs) = *dptr;
180  if(beptr) {
181  strb[i] = *beptr == 0xff;
182  ++beptr;
183  } else
184  strb[i] = true;
185  }
186  } else {
187  auto beat_start_idx = byte_offset - offset;
188  auto data_len = trans.get_data_length();
189  auto dptr = trans.get_data_ptr() + beat_start_idx;
190  for(size_t i = 0; i < size && (beat_start_idx + i) < data_len; ++i, ++dptr) {
191  auto bit_offs = i * 8;
192  data(bit_offs + 7, bit_offs) = *dptr;
193  if(beptr) {
194  strb[i] = *beptr == 0xff;
195  ++beptr;
196  } else
197  strb[i] = true;
198  }
199  }
200  } else { // aligned or single beat access
201  auto dptr = trans.get_data_ptr() + byte_offset;
202  for(size_t i = 0; i < size; ++i, ++dptr) {
203  auto bit_offs = (offset + i) * 8;
204  data(bit_offs + 7, bit_offs) = *dptr;
205  if(beptr) {
206  strb[offset + i] = *beptr == 0xff;
207  ++beptr;
208  } else
209  strb[offset + i] = true;
210  }
211  }
212  this->w_data.write(data);
213  this->w_strb.write(strb);
214  if(!CFG::IS_LITE) {
215  this->w_id->write(ext->get_id());
216  if(this->w_user.get_interface())
217  this->w_user->write(ext->get_user(axi::common::id_type::DATA));
218  }
219 }
220 
221 template <typename CFG> inline void axi::pin::axi4_initiator<CFG>::setup_callbacks(fsm_handle* fsm_hndl) {
222  fsm_hndl->fsm->cb[RequestPhaseBeg] = [this, fsm_hndl]() -> void {
223  fsm_hndl->beat_count = 0;
224  outstanding_cnt[fsm_hndl->trans->get_command()]++;
225  if(CFG::IS_LITE) {
226  auto offset = fsm_hndl->trans->get_address() % (CFG::BUSWIDTH / 8);
227  if(offset + fsm_hndl->trans->get_data_length() > CFG::BUSWIDTH / 8) {
228  SCCFATAL(SCMOD) << " transaction " << *fsm_hndl->trans << " is not AXI4Lite compliant";
229  }
230  }
231  };
232  fsm_hndl->fsm->cb[BegPartReqE] = [this, fsm_hndl]() -> void {
233  sc_assert(fsm_hndl->trans->is_write());
234  if(fsm_hndl->beat_count == 0) {
235  write_aw(*fsm_hndl->trans);
236  aw_evt.notify(sc_core::SC_ZERO_TIME);
237  }
238  write_wdata(*fsm_hndl->trans, fsm_hndl->beat_count);
239  active_req[tlm::TLM_WRITE_COMMAND] = fsm_hndl;
240  wdata_vl.write(0x1);
241  };
242  fsm_hndl->fsm->cb[EndPartReqE] = [this, fsm_hndl]() -> void {
243  active_req[tlm::TLM_WRITE_COMMAND] = nullptr;
244  tlm::tlm_phase phase = axi::END_PARTIAL_REQ;
245  sc_core::sc_time t(clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : sc_core::SC_ZERO_TIME);
246  auto ret = tsckt->nb_transport_bw(*fsm_hndl->trans, phase, t);
247  fsm_hndl->beat_count++;
248  };
249  fsm_hndl->fsm->cb[BegReqE] = [this, fsm_hndl]() -> void {
250  switch(fsm_hndl->trans->get_command()) {
251  case tlm::TLM_READ_COMMAND:
252  active_req[tlm::TLM_READ_COMMAND] = fsm_hndl;
253  write_ar(*fsm_hndl->trans);
254  ar_evt.notify(sc_core::SC_ZERO_TIME);
255  break;
256  case tlm::TLM_WRITE_COMMAND:
257  active_req[tlm::TLM_WRITE_COMMAND] = fsm_hndl;
258  if(fsm_hndl->beat_count == 0) {
259  write_aw(*fsm_hndl->trans);
260  aw_evt.notify(sc_core::SC_ZERO_TIME);
261  }
262  write_wdata(*fsm_hndl->trans, fsm_hndl->beat_count, true);
263  wdata_vl.write(0x3);
264  }
265  };
266  fsm_hndl->fsm->cb[EndReqE] = [this, fsm_hndl]() -> void {
267  auto id = axi::get_axi_id(*fsm_hndl->trans);
268  switch(fsm_hndl->trans->get_command()) {
269  case tlm::TLM_READ_COMMAND:
270  rd_resp_by_id[id].push_back(fsm_hndl);
271  active_req[tlm::TLM_READ_COMMAND] = nullptr;
272  break;
273  case tlm::TLM_WRITE_COMMAND:
274  wr_resp_by_id[id].push_back(fsm_hndl);
275  active_req[tlm::TLM_WRITE_COMMAND] = nullptr;
276  fsm_hndl->beat_count++;
277  }
278  tlm::tlm_phase phase = tlm::END_REQ;
279  sc_core::sc_time t(clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : sc_core::SC_ZERO_TIME);
280  auto ret = tsckt->nb_transport_bw(*fsm_hndl->trans, phase, t);
281  fsm_hndl->trans->set_response_status(tlm::TLM_OK_RESPONSE);
282  };
283  fsm_hndl->fsm->cb[BegPartRespE] = [this, fsm_hndl]() -> void {
284  // scheduling the response
285  assert(fsm_hndl->trans->is_read());
286  tlm::tlm_phase phase = axi::BEGIN_PARTIAL_RESP;
287  sc_core::sc_time t(sc_core::SC_ZERO_TIME);
288  auto ret = tsckt->nb_transport_bw(*fsm_hndl->trans, phase, t);
289  };
290  fsm_hndl->fsm->cb[EndPartRespE] = [this, fsm_hndl]() -> void {
291  fsm_hndl->beat_count++;
292  r_end_resp_evt.notify();
293  };
294  fsm_hndl->fsm->cb[BegRespE] = [this, fsm_hndl]() -> void {
295  // scheduling the response
296  tlm::tlm_phase phase = tlm::BEGIN_RESP;
297  sc_core::sc_time t(sc_core::SC_ZERO_TIME);
298  auto ret = tsckt->nb_transport_bw(*fsm_hndl->trans, phase, t);
299  };
300  fsm_hndl->fsm->cb[EndRespE] = [this, fsm_hndl]() -> void {
301  r_end_resp_evt.notify();
302  if(fsm_hndl->trans->is_read())
303  rd_resp_by_id[axi::get_axi_id(*fsm_hndl->trans)].pop_front();
304  if(fsm_hndl->trans->is_write())
305  wr_resp_by_id[axi::get_axi_id(*fsm_hndl->trans)].pop_front();
306  };
307 }
308 
309 template <typename CFG> inline void axi::pin::axi4_initiator<CFG>::ar_t() {
310  this->ar_valid.write(false);
311  wait(sc_core::SC_ZERO_TIME);
312  while(true) {
313  wait(ar_evt);
314  this->ar_valid.write(true);
315  do {
316  wait(this->ar_ready.posedge_event() | clk_delayed);
317  if(this->ar_ready.read())
318  react(axi::fsm::protocol_time_point_e::EndReqE, active_req[tlm::TLM_READ_COMMAND]);
319  } while(!this->ar_ready.read());
320  wait(clk_i.posedge_event());
321  this->ar_valid.write(false);
322  }
323 }
324 
325 template <typename CFG> inline void axi::pin::axi4_initiator<CFG>::r_t() {
326  this->r_ready.write(false);
327  wait(sc_core::SC_ZERO_TIME);
328  while(true) {
329  if(!this->r_valid.read())
330  wait(this->r_valid.posedge_event());
331  else
332  wait(clk_delayed);
333  if(this->r_valid.event() || (!active_resp[tlm::TLM_READ_COMMAND] && this->r_valid.read())) {
334  wait(sc_core::SC_ZERO_TIME);
335  auto id = CFG::IS_LITE ? 0U : this->r_id->read().to_uint();
336  auto data = this->r_data.read();
337  auto resp = this->r_resp.read();
338  auto& q = rd_resp_by_id[id];
339  sc_assert(q.size() && "No transaction found for received id");
340  auto* fsm_hndl = q.front();
341  auto beat_count = fsm_hndl->beat_count;
342  auto size = axi::get_burst_size(*fsm_hndl->trans);
343  auto byte_offset = beat_count * size;
344  auto offset = (fsm_hndl->trans->get_address() + byte_offset) & (CFG::BUSWIDTH / 8 - 1);
345  if(offset && (size + offset) > (CFG::BUSWIDTH / 8)) { // un-aligned multi-beat access
346  if(beat_count == 0) {
347  auto dptr = fsm_hndl->trans->get_data_ptr();
348  for(size_t i = offset; i < size; ++i, ++dptr) {
349  auto bit_offs = i * 8;
350  *dptr = data(bit_offs + 7, bit_offs).to_uint();
351  }
352  } else {
353  auto beat_start_idx = beat_count * size - offset;
354  auto data_len = fsm_hndl->trans->get_data_length();
355  auto dptr = fsm_hndl->trans->get_data_ptr() + beat_start_idx;
356  for(size_t i = offset; i < size && (beat_start_idx + i) < data_len; ++i, ++dptr) {
357  auto bit_offs = i * 8;
358  *dptr = data(bit_offs + 7, bit_offs).to_uint();
359  }
360  }
361  } else { // aligned or single beat access
362  auto dptr = fsm_hndl->trans->get_data_ptr() + beat_count * size;
363  for(size_t i = 0; i < size; ++i, ++dptr) {
364  auto bit_offs = (offset + i) * 8;
365  *dptr = data(bit_offs + 7, bit_offs).to_uint();
366  }
367  }
369  fsm_hndl->trans->get_extension(e);
370  e->set_resp(axi::into<axi::resp_e>(resp));
371  e->add_to_response_array(*e);
372  auto tp = CFG::IS_LITE || this->r_last->read() ? axi::fsm::protocol_time_point_e::BegRespE
373  : axi::fsm::protocol_time_point_e::BegPartRespE;
374  react(tp, fsm_hndl);
375  wait(r_end_resp_evt);
376  this->r_ready.write(true);
377  wait(clk_i.posedge_event());
378  this->r_ready.write(false);
379  }
380  }
381 }
382 
383 template <typename CFG> inline void axi::pin::axi4_initiator<CFG>::aw_t() {
384  this->aw_valid.write(false);
385  wait(sc_core::SC_ZERO_TIME);
386  while(true) {
387  wait(aw_evt);
388  this->aw_valid.write(true);
389  do {
390  wait(this->aw_ready.posedge_event() | clk_delayed);
391  } while(!this->aw_ready.read());
392  wait(clk_i.posedge_event());
393  this->aw_valid.write(false);
394  }
395 }
396 
397 template <typename CFG> inline void axi::pin::axi4_initiator<CFG>::wdata_t() {
398  this->w_valid.write(false);
399  wait(sc_core::SC_ZERO_TIME);
400  while(true) {
401  if(!CFG::IS_LITE)
402  this->w_last->write(false);
403  wait(wdata_vl.default_event());
404  auto val = wdata_vl.read();
405  this->w_valid.write(val & 0x1);
406  if(!CFG::IS_LITE)
407  this->w_last->write(val & 0x2);
408  do {
409  wait(this->w_ready.posedge_event() | clk_delayed);
410  if(this->w_ready.read()) {
411  auto evt =
412  CFG::IS_LITE || (val & 0x2) ? axi::fsm::protocol_time_point_e::EndReqE : axi::fsm::protocol_time_point_e::EndPartReqE;
413  react(evt, active_req[tlm::TLM_WRITE_COMMAND]);
414  }
415  } while(!this->w_ready.read());
416  wait(clk_i.posedge_event());
417  this->w_valid.write(false);
418  }
419 }
420 
421 template <typename CFG> inline void axi::pin::axi4_initiator<CFG>::b_t() {
422  this->b_ready.write(false);
423  wait(sc_core::SC_ZERO_TIME);
424  while(true) {
425  wait(this->b_valid.posedge_event() | clk_delayed);
426  if(this->b_valid.event() || (!active_resp[tlm::TLM_WRITE_COMMAND] && this->b_valid.read())) {
427  auto id = !CFG::IS_LITE ? this->b_id->read().to_uint() : 0U;
428  auto resp = this->b_resp.read();
429  auto& q = wr_resp_by_id[id];
430  sc_assert(q.size());
431  auto* fsm_hndl = q.front();
433  fsm_hndl->trans->get_extension(e);
434  e->set_resp(axi::into<axi::resp_e>(resp));
435  react(axi::fsm::protocol_time_point_e::BegRespE, fsm_hndl);
436  wait(r_end_resp_evt);
437  this->b_ready.write(true);
438  wait(clk_i.posedge_event());
439  this->b_ready.write(false);
440  }
441  }
442 }
443 
444 #endif /* _BUS_AXI_PIN_AXI4_INITIATOR_H_ */
TLM2.0 components modeling AHB.
Definition: axi_initiator.h:30
tlm::tlm_fw_transport_if< TYPES > axi_fw_transport_if
alias declaration for the forward interface
Definition: axi_tlm.h:916
constexpr ULT to_int(E t)
Definition: axi_tlm.h:47
unsigned get_burst_size(const request &r)
Definition: axi_tlm.h:1157
void add_to_response_array(response &)
add a read response to the response array
Definition: axi_tlm.h:1598
base class of all AXITLM based adapters and interfaces.
Definition: base.h:43
tlm::tlm_sync_enum nb_fw(payload_type &trans, phase_type const &phase, sc_core::sc_time &t)
triggers the FSM based on TLM phases in the forward path. Should be called from np_transport_fw of th...
Definition: base.cpp:190
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
uint8_t get_size() const
set the AxSIZE value of the transaction
Definition: axi_tlm.h:1389
void set_resp(resp_e)
set the response status as POD
Definition: axi_tlm.h:1500