scc  2024.06
SystemC components library
cxs_tlm.h
1 /*******************************************************************************
2  * Copyright 2024 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 _CXS_CXS_TLM_H_
18 #define _CXS_CXS_TLM_H_
19 
20 #include <cci_configuration>
21 #include <cstdint>
22 #include <scc/fifo_w_cb.h>
23 #include <scc/peq.h>
24 #include <scc/report.h>
25 #include <scc/sc_variable.h>
26 #include <tlm/nw/tlm_network_gp.h>
27 #include <tlm/nw/tlm_network_sockets.h>
28 #include <tlm/scc/tlm_gp_shared.h>
29 #include <tlm/scc/tlm_mm.h>
30 
32 namespace cxs {
33 enum class CXS_CMD { FLIT, CREDIT, CRDRTN };
34 
36  cxs_flit_payload() = default;
37 
40 
41  std::array<uint8_t, 8> start_ptr;
42  std::array<uint8_t, 8> end_ptr;
43  uint8_t start{0};
44  uint8_t end{0};
45  uint8_t end_error{0};
46  bool last{false};
47 };
48 
51  using tlm_phase_type = ::tlm::tlm_phase;
52 };
53 
54 enum class CXS_PKT { DATA };
56  cxs_packet_payload() = default;
57 
60 };
61 
64  using tlm_phase_type = ::tlm::tlm_phase;
65 };
66 
67 } // namespace cxs
68 
69 namespace tlm {
70 namespace scc {
71 // provide needed info for SCC memory manager
72 template <> struct tlm_mm_traits<cxs::cxs_flit_types> {
75 };
76 template <> struct tlm_mm_traits<cxs::cxs_packet_types> {
79 };
80 } // namespace scc
81 } // namespace tlm
82 
83 namespace cxs {
84 template <unsigned PHITWIDTH = 256, int N = 1>
86 template <unsigned PHITWIDTH = 256, int N = 1>
88 using cxs_flit_shared_ptr = tlm::scc::tlm_payload_shared_ptr<cxs_flit_payload>;
89 using cxs_flit_mm = tlm::scc::tlm_mm<cxs_flit_types, false>;
90 
91 template <int N = 1> using cxs_pkt_initiator_socket = tlm::nw::tlm_network_initiator_socket<8, CXS_PKT, cxs_packet_types, N>;
92 template <int N = 1> using cxs_pkt_target_socket = tlm::nw::tlm_network_target_socket<8, CXS_PKT, cxs_packet_types, N>;
95 
96 struct orig_pkt_extension : public tlm::tlm_extension<orig_pkt_extension> {
97  virtual tlm_extension_base* clone() const override {
98  auto ret = new orig_pkt_extension();
99  *ret = *this;
100  return ret;
101  }
102  void copy_from(tlm_extension_base const& ext) override { *this = dynamic_cast<orig_pkt_extension const&>(ext); }
103  virtual ~orig_pkt_extension() = default;
104 
105  std::vector<cxs_pkt_shared_ptr> orig_ext;
106 };
107 
108 template <unsigned PHITWIDTH = 256, unsigned CXSMAXPKTPERFLIT = 2>
109 struct cxs_transmitter : public sc_core::sc_module,
110  public tlm::nw::tlm_network_fw_transport_if<cxs_packet_types>,
111  public tlm::nw::tlm_network_bw_transport_if<cxs_flit_types> {
113  using flit_phase_type = cxs_flit_types::tlm_phase_type;
114 
116  using pkt_phase_type = cxs_packet_types::tlm_phase_type;
117 
118  static constexpr unsigned PHIT_BYTE_WIDTH = PHITWIDTH / 8;
119  static constexpr unsigned BUCKET_SIZE = PHITWIDTH / 8 / CXSMAXPKTPERFLIT;
120 
121  sc_core::sc_in<bool> clk_i{"clk_i"};
122 
123  sc_core::sc_in<bool> rst_i{"rst_i"};
124 
125  cxs_pkt_target_socket<> tsck{"tsck"};
126 
128 
129  cci::cci_param<sc_core::sc_time> clock_period{"clock_period", sc_core::SC_ZERO_TIME, "clock period of the CXS transmitter"};
130 
131  cci::cci_param<unsigned> burst_len{"burst_len", 1, "minimum amount of credits to start transmitting flits"};
132 
133  cxs_transmitter(sc_core::sc_module_name const& nm)
134  : sc_core::sc_module(nm) {
135  tsck(*this);
136  isck(*this);
137  SC_HAS_PROCESS(cxs_transmitter);
138  SC_METHOD(clock);
139  sensitive << clk_i.pos();
140  SC_METHOD(reset);
141  sensitive << rst_i;
142  dont_initialize();
143  }
144 
145 private:
146  void start_of_simulation() override {
147  if(clock_period.get_value() == sc_core::SC_ZERO_TIME)
148  if(auto clk_if = dynamic_cast<sc_core::sc_clock*>(clk_i.get_interface()))
149  clock_period.set_value(clk_if->period());
150  }
151 
152  void b_transport(pkt_tx_type& trans, sc_core::sc_time& t) override {
153  flit_tx_type tx;
154  auto ext = new orig_pkt_extension();
155  tx.set_extension(ext);
156  ext->orig_ext.emplace_back(&trans);
157  isck->b_transport(tx, t);
158  }
159 
160  tlm::tlm_sync_enum nb_transport_fw(pkt_tx_type& trans, pkt_phase_type& phase, sc_core::sc_time& t) override {
161  SCCTRACE(SCMOD) << "Forwarding CXS packet with size " << trans.get_data().size() << "bytes";
162  if(phase == tlm::nw::REQUEST) {
163  pkt_peq.notify(cxs_pkt_shared_ptr(&trans), t);
164  phase = tlm::nw::CONFIRM;
165  return tlm::TLM_UPDATED;
166  }
167  throw std::runtime_error("illegal request in forward path");
168  }
169 
170  unsigned int transport_dbg(pkt_tx_type& trans) override { return 0; }
171 
172  tlm::tlm_sync_enum nb_transport_bw(flit_tx_type& trans, flit_phase_type& phase, sc_core::sc_time& t) override {
173  SCCTRACE(SCMOD) << "Received non-blocking transaction in bw path with phase " << phase.get_name();
174  if(phase == tlm::nw::REQUEST) {
175  received_credits += trans.get_data()[0];
176  SCCDEBUG(SCMOD) << "Received " << static_cast<unsigned>(trans.get_data()[0]) << " credit(s), " << received_credits.get()
177  << " credit(s) in total";
178  phase = tlm::nw::CONFIRM;
179  return tlm::TLM_UPDATED;
180  }
181  throw std::runtime_error("illegal request in backward path");
182  }
183 
184  void clock() {
185  if((!pending_pkt && !pkt_peq.has_next()) || // there are no packets to send
186  (received_credits < burst_len.get_value() && !burst_credits) ||
187  rst_i.read()) // we do not have enough credits to send them as burst
188  return;
189  auto* ptr = cxs_flit_mm::get().allocate();
190  auto ext = ptr->get_extension<orig_pkt_extension>();
191  if(!ext) {
192  ext = new orig_pkt_extension();
193  ptr->set_auto_extension(ext);
194  }
195  auto next_bucket = 0U;
196  auto start_ptr_idx = 0U;
197  auto end_ptr_idx = 0U;
198  while(pkt_peq.has_next() || pending_pkt) {
199  auto trans = pending_pkt ? pending_pkt : pkt_peq.get();
200  pending_pkt = nullptr;
201  if(!transfered_pkt_bytes) { // we start a new packet
202  ptr->start |= 1u << start_ptr_idx;
203  ptr->start_ptr[start_ptr_idx++] = next_bucket;
204  }
205  const auto remaining_bytes = trans->get_data().size() - transfered_pkt_bytes;
206  const auto remaining_buckets = (CXSMAXPKTPERFLIT - next_bucket);
207  const auto bucketed_size = (remaining_bytes + BUCKET_SIZE - 1) / BUCKET_SIZE;
208  if(bucketed_size > remaining_buckets) {
209  // packet exceeds current flit, so sen the flit
210  transfered_pkt_bytes += remaining_buckets * BUCKET_SIZE;
211  pending_pkt = trans;
212  break;
213  } else {
214  // packet fits into current flit length
215  ptr->end |= 1u << end_ptr_idx;
216  ptr->end_ptr[end_ptr_idx++] = (next_bucket * BUCKET_SIZE + remaining_bytes + 1) / 4 - 1; // end pointer is 4 byte aligned
217  ptr->last = true;
218  next_bucket += bucketed_size;
219  ext->orig_ext.push_back(trans);
220  transfered_pkt_bytes = 0;
221  }
222  }
223  sc_core::sc_time t;
224  auto phase = tlm::nw::REQUEST;
225  isck->nb_transport_fw(*ptr, phase, t);
226  if(!burst_credits) {
227  burst_credits = burst_len.get_value();
228  received_credits -= burst_credits;
229  }
230  burst_credits--;
231  }
232 
233  void reset() {
234  if(rst_i.read()) {
235  received_credits = 0;
236  pkt_peq.clear();
237  pending_pkt = nullptr;
238  }
239  }
240 
242  cxs_pkt_shared_ptr pending_pkt;
243  unsigned transfered_pkt_bytes{0};
244 
245  scc::sc_variable<unsigned> received_credits{"received_credits", 0};
246  unsigned burst_credits{0};
247 };
248 
249 template <unsigned PHITWIDTH = 64, unsigned CXSMAXPKTPERFLIT = 2>
250 struct cxs_receiver : public sc_core::sc_module,
251  public tlm::nw::tlm_network_fw_transport_if<cxs_flit_types>,
252  public tlm::nw::tlm_network_bw_transport_if<cxs_packet_types> {
254  using flit_phase_type = cxs_flit_types::tlm_phase_type;
255 
257  using pkt_phase_type = cxs_packet_types::tlm_phase_type;
258 
259  static constexpr unsigned PHIT_BYTE_WIDTH = PHITWIDTH / 8;
260  static constexpr unsigned BUCKET_SIZE = PHITWIDTH / 8 / CXSMAXPKTPERFLIT;
261 
262  sc_core::sc_in<bool> clk_i{"clk_i"};
263 
264  sc_core::sc_in<bool> rst_i{"rst_i"};
265 
267 
268  cxs_pkt_initiator_socket<> isck{"isck"};
269 
270  cci::cci_param<sc_core::sc_time> clock_period{"clock_period", sc_core::SC_ZERO_TIME, "clock period of the CXS receiver"};
271 
272  cci::cci_param<unsigned> max_credit{"max_credits", 1, "CXS_MAX_CREDIT property"};
273 
274  cxs_receiver(sc_core::sc_module_name const& nm)
275  : sc_core::sc_module(nm) {
276  tsck(*this);
277  isck(*this);
278  SC_HAS_PROCESS(cxs_receiver);
279  SC_METHOD(clock);
280  sensitive << clk_i.pos();
281  dont_initialize();
282  SC_METHOD(reset);
283  sensitive << rst_i;
284  dont_initialize();
285  SC_METHOD(send_credit);
286  sensitive << credit_returned.data_written_event();
287  dont_initialize();
288  }
289 
290 private:
291  void start_of_simulation() override {
292  if(clock_period.get_value() == sc_core::SC_ZERO_TIME)
293  if(auto clk_if = dynamic_cast<sc_core::sc_clock*>(clk_i.get_interface()))
294  clock_period.set_value(clk_if->period());
295  }
296 
297  void b_transport(flit_tx_type& trans, sc_core::sc_time& t) override {
298  auto ext = trans.get_extension<orig_pkt_extension>();
299  sc_assert(ext);
300  auto tx = ext->orig_ext.front();
301  isck->b_transport(*tx, t);
302  }
303 
304  tlm::tlm_sync_enum nb_transport_fw(flit_tx_type& trans, flit_phase_type& phase, sc_core::sc_time& t) override {
305  SCCTRACE(SCMOD) << "Received non-blocking transaction in fw path with phase " << phase.get_name();
306  credit_returned.push_back(1);
307  if(trans.end) {
308  auto ext = trans.get_extension<cxs::orig_pkt_extension>();
309  for(auto& orig_ptr : ext->orig_ext) {
310  auto ph = tlm::nw::REQUEST;
311  auto d = sc_core::SC_ZERO_TIME;
312  SCCTRACE(SCMOD) << "Forwarding CXS pkt with size " << orig_ptr->get_data().size() << "bytes";
313  auto status = isck->nb_transport_fw(*orig_ptr, ph, t);
314  sc_assert(status == tlm::TLM_UPDATED);
315  }
316  }
317  phase = tlm::nw::RESPONSE;
318  if(clock_period.get_value() != sc_core::SC_ZERO_TIME)
319  t += clock_period.get_value() - 1_ps;
320  return tlm::TLM_UPDATED;
321  }
322 
323  unsigned int transport_dbg(flit_tx_type& trans) override { return 0; }
324 
325  tlm::tlm_sync_enum nb_transport_bw(pkt_tx_type& trans, flit_phase_type& phase, sc_core::sc_time& t) override {
326  SCCTRACE(SCMOD) << "Received non-blocking transaction in bw path with phase " << phase.get_name();
327  if(phase == tlm::nw::CONFIRM)
328  return tlm::TLM_ACCEPTED;
329  throw std::runtime_error("illegal request in backward path");
330  }
331 
332  void reset() {
333  if(rst_i.read()) {
334  credit_returned.push_back(max_credit.get_value());
335  }
336  }
337 
338  void send_credit() {
339  auto amount = credit_returned.front();
340  credit_returned.pop_front();
341  available_credits += amount;
342  }
343 
344  void clock() {
345  if(rst_i.read())
346  return;
347  if(available_credits > 0) {
348  auto* ptr = cxs_flit_mm::get().allocate();
349  ptr->set_command(cxs::CXS_CMD::CREDIT);
350  ptr->set_data({1});
351  auto ph = tlm::nw::REQUEST;
352  auto t = sc_core::SC_ZERO_TIME;
353  auto status = tsck->nb_transport_bw(*ptr, ph, t);
354  sc_assert(status == tlm::TLM_UPDATED);
355  available_credits -= 1;
356  }
357  }
358  scc::sc_variable<unsigned> available_credits{"available_credits", 0};
359  scc::fifo_w_cb<unsigned char> credit_returned;
360 };
361 
362 template <unsigned PHITWIDTH = 64>
363 struct cxs_channel : public sc_core::sc_module,
364  public tlm::nw::tlm_network_fw_transport_if<cxs_flit_types>,
365  public tlm::nw::tlm_network_bw_transport_if<cxs_flit_types> {
366 
368  using phase_type = cxs_flit_types::tlm_phase_type;
369 
371 
373 
374  cci::cci_param<sc_core::sc_time> channel_delay{"channel_delay", sc_core::SC_ZERO_TIME, "delay of the CXS channel"};
375 
376  cci::cci_param<sc_core::sc_time> tx_clock_period{"tx_clock_period", sc_core::SC_ZERO_TIME,
377  "receiver side clock period of the CXS channel"};
378 
379  cci::cci_param<sc_core::sc_time> rx_clock_period{"rx_clock_period", sc_core::SC_ZERO_TIME,
380  "receiver side clock period of the CXS channel"};
381 
382  cxs_channel(sc_core::sc_module_name const& nm)
383  : sc_core::sc_module(nm) {
384  isck(*this);
385  tsck(*this);
386  SC_HAS_PROCESS(cxs_channel);
387  SC_METHOD(fw);
388  SC_METHOD(bw);
389  }
390 
391  void b_transport(transaction_type& trans, sc_core::sc_time& t) override {
392  t += channel_delay;
393  isck->b_transport(trans, t);
394  }
395 
396  tlm::tlm_sync_enum nb_transport_fw(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) override {
397  SCCTRACE(SCMOD) << "Received non-blocking transaction in fw path with phase " << phase.get_name();
398  if(phase == tlm::nw::REQUEST) {
399  if(trans.get_data().size() > PHITWIDTH / 8) {
400  SCCERR(SCMOD) << "A CXS flit can be maximal " << PHITWIDTH / 8 << " bytes long, current data length is "
401  << trans.get_data().size() << " bytes";
402  }
403  fw_peq.notify(cxs_flit_shared_ptr(&trans), channel_delay.get_value());
404  if(rx_clock_period.get_value() > sc_core::SC_ZERO_TIME)
405  t += rx_clock_period - 1_ps;
406  phase = tlm::nw::CONFIRM;
407  return tlm::TLM_UPDATED;
408  } else if(phase == tlm::nw::RESPONSE) { // a credit response
409  bw_resp.notify(sc_core::SC_ZERO_TIME);
410  return tlm::TLM_ACCEPTED;
411  }
412  throw std::runtime_error("illegal request in forward path");
413  }
414 
415  tlm::tlm_sync_enum nb_transport_bw(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) override {
416  SCCTRACE(SCMOD) << "Received non-blocking transaction in bw path with phase " << phase.get_name();
417  if(phase == tlm::nw::REQUEST) { // this is a credit
418  SCCDEBUG(SCMOD) << "Forwarding " << static_cast<unsigned>(trans.get_data()[0]) << " credit(s)";
419  bw_peq.notify(cxs_flit_shared_ptr(&trans), channel_delay.get_value());
420  if(tx_clock_period.get_value() > sc_core::SC_ZERO_TIME)
421  t += tx_clock_period - 1_ps;
422  phase = tlm::nw::CONFIRM;
423  return tlm::TLM_UPDATED;
424  } else if(phase == tlm::nw::RESPONSE) { // a data transfer completion
425  fw_resp.notify(sc_core::SC_ZERO_TIME);
426  return tlm::TLM_ACCEPTED;
427  }
428  throw std::runtime_error("illegal response in backward path");
429  }
430 
431  unsigned int transport_dbg(transaction_type& trans) override { return isck->transport_dbg(trans); }
432 
433 private:
434  void fw() {
435  while(fw_peq.has_next()) {
436  auto ptr = fw_peq.get();
437  auto phase = tlm::nw::INDICATION;
438  auto t = sc_core::SC_ZERO_TIME;
439  auto sync = isck->nb_transport_fw(*ptr, phase, t);
440  if(sync == tlm::TLM_ACCEPTED) {
441  next_trigger(fw_resp);
442  return;
443  } else {
444  sc_assert(sync == tlm::TLM_UPDATED || sync == tlm::TLM_COMPLETED);
445  }
446  }
447  next_trigger(fw_peq.event());
448  }
449 
450  void bw() {
451  while(bw_peq.has_next()) {
452  auto ptr = bw_peq.get();
453  auto ph{tlm::nw::REQUEST};
454  sc_core::sc_time d;
455  auto sync = tsck->nb_transport_bw(*ptr, ph, d);
456  if(sync == tlm::TLM_ACCEPTED) {
457  next_trigger(bw_resp);
458  return;
459  } else {
460  sc_assert(sync == tlm::TLM_UPDATED || sync == tlm::TLM_COMPLETED);
461  }
462  }
463  next_trigger(bw_peq.event());
464  }
465 
468  sc_core::sc_event fw_resp, bw_resp;
469 };
470 } // namespace cxs
471 #endif // _CXS_CXS_TLM_H_
payload_type * allocate()
get a plain tlm_payload_type without extensions
Definition: tlm_mm.h:185
CXS TLM utilities.
Definition: cxs_tlm.h:32
SCC TLM utilities.
SystemC TLM.
Definition: cxs_tlm.h:69
priority event queue
Definition: peq.h:41
T get() const
value getter
Definition: sc_variable.h:124
a tlm memory manager
Definition: tlm_mm.h:285
static tlm_mm & get()
accessor function of the singleton
Definition: tlm_mm.h:293