scc  2024.06
SystemC components library
target.h
1 /*******************************************************************************
2  * Copyright 2021 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 #pragma once
18 
19 #include <interfaces/obi/obi_tlm.h>
20 #include <scc/mt19937_rng.h>
21 #include <scc/peq.h>
22 #include <scc/report.h>
23 #include <scc/signal_opt_ports.h>
24 #include <systemc>
25 #include <tlm/scc/initiator_mixin.h>
26 #include <tlm/scc/scv/tlm_rec_initiator_socket.h>
27 #include <tlm/scc/tlm_gp_shared.h>
28 #include <tlm/scc/tlm_id.h>
29 #include <tlm/scc/tlm_mm.h>
30 #include <tlm>
31 
32 #include <memory>
33 #include <queue>
34 #include <tuple>
35 #include <unordered_map>
36 
37 namespace obi {
38 
39 namespace pin {
40 template <unsigned int DATA_WIDTH = 32, unsigned int ADDR_WIDTH = 32, unsigned int ID_WIDTH = 0, unsigned int USER_WIDTH = 0>
41 class target : public sc_core::sc_module {
42 public:
43  using payload_type = tlm::tlm_base_protocol_types::tlm_payload_type;
44  using phase_type = tlm::tlm_base_protocol_types::tlm_phase_type;
45 
46  SC_HAS_PROCESS(target);
47 
48  target(sc_core::sc_module_name nm);
49 
51  // Global signals
52  sc_core::sc_in<bool> clk_i{"clk_i"};
53  sc_core::sc_in<bool> resetn_i{"resetn_i"}; // active low reset
54  // A Channel signals
55  sc_core::sc_in<bool> req_i{"req_i"};
56  sc_core::sc_out<bool> gnt_o{"gnt_o"};
57  sc_core::sc_in<sc_dt::sc_uint<ADDR_WIDTH>> addr_i{"addr_i"};
58  sc_core::sc_in<bool> we_i{"we_i"};
59  sc_core::sc_in<sc_dt::sc_uint<DATA_WIDTH / 8>> be_i{"be_i"};
60  sc_core::sc_in<sc_dt::sc_uint<DATA_WIDTH>> wdata_i{"wdata_i"};
64  // R Channel signals
65  sc_core::sc_out<bool> rvalid_o{"rvalid_o"};
66  sc_core::sc_in<bool> rready_i{"rready_i"};
67  sc_core::sc_out<sc_dt::sc_uint<DATA_WIDTH>> rdata_o{"rdata_o"};
68  sc_core::sc_out<bool> err_o{"err_o"};
71 
72  tlm::tlm_sync_enum nb_transport_bw(payload_type& trans, phase_type& phase, sc_core::sc_time& t);
73 
74  cci::cci_param<sc_core::sc_time> sample_delay{"sample_delay", 0_ns};
75  cci::cci_param<int> req2gnt_delay{"req2gnt_delay", 0};
76  cci::cci_param<int> addr2data_delay{"addr2data_delay", 0};
77 
78 private:
79  void clk_cb();
80  void achannel_req_t();
81  void rchannel_rsp_t();
82 
83  void clk_delay() {
84  if(sc_core::sc_delta_count_at_current_time() < 5) {
85  clk_self.notify(sc_core::SC_ZERO_TIME);
86  next_trigger(clk_self);
87  } else
88  clk_delayed.notify(sc_core::SC_ZERO_TIME /*clk_if ? clk_if->period() - 1_ps : 1_ps*/);
89  }
90  sc_core::sc_event clk_delayed, clk_self;
92  std::deque<std::tuple<tlm::scc::tlm_gp_shared_ptr, unsigned>> rchannel_pending_rsp;
94  struct tx_state {
95  bool addrPhaseFinished;
96  tlm::tlm_phase last_phase;
97  tlm::scc::tlm_gp_shared_ptr pending_tx;
98  };
99  std::unordered_map<payload_type*, tx_state> states;
100 };
101 
103 // Class definition
105 template <unsigned int DATA_WIDTH, unsigned int ADDR_WIDTH, unsigned int ID_WIDTH, unsigned int USER_WIDTH>
107 : sc_module(nm) {
108  isckt.register_nb_transport_bw([this](payload_type& trans, phase_type& phase, sc_core::sc_time& t) -> tlm::tlm_sync_enum {
109  return nb_transport_bw(trans, phase, t);
110  });
111  SC_METHOD(clk_cb)
112  sensitive << clk_i.pos() << resetn_i.neg();
113  SC_METHOD(clk_delay);
114  sensitive << clk_i.pos();
115  SC_THREAD(achannel_req_t)
116  SC_THREAD(rchannel_rsp_t);
117 }
118 
119 template <unsigned int DATA_WIDTH, unsigned int ADDR_WIDTH, unsigned int ID_WIDTH, unsigned int USER_WIDTH>
120 inline void target<DATA_WIDTH, ADDR_WIDTH, ID_WIDTH, USER_WIDTH>::target::clk_cb() {
121  sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
122  if(clk_i.event()) {
123  if(rchannel_pending_rsp.size()) {
124  auto& head = rchannel_pending_rsp.front();
125  if(std::get<1>(head) == 0) {
126  rchannel_rsp.notify(std::get<0>(head));
127  rchannel_pending_rsp.pop_front();
128  }
129  for(auto& e : rchannel_pending_rsp) {
130  if(std::get<1>(e))
131  --std::get<1>(e);
132  }
133  }
134  }
135 }
136 
137 template <unsigned int DATA_WIDTH, unsigned int ADDR_WIDTH, unsigned int ID_WIDTH, unsigned int USER_WIDTH>
138 inline tlm::tlm_sync_enum
139 target<DATA_WIDTH, ADDR_WIDTH, ID_WIDTH, USER_WIDTH>::target::nb_transport_bw(payload_type& trans, phase_type& phase, sc_core::sc_time& t) {
140  auto id = obi::get_obi_id(trans);
141  auto* ext = trans.get_extension<obi::obi_extension>();
142  sc_assert(ext && "obi_extension missing");
143  switch(phase) {
144  case tlm::END_REQ: {
145  auto it = states.find(&trans);
146  sc_assert(it != states.end());
147  it->second.last_phase = tlm::END_REQ;
148  achannel_rsp.notify(&trans, t);
149  return tlm::TLM_ACCEPTED;
150  }
151  case tlm::BEGIN_RESP: {
152  auto it = states.find(&trans);
153  sc_assert(it != states.end());
154  auto& state = it->second;
155  if(state.pending_tx) {
156  unsigned resp_delay = addr2data_delay < 0 ? scc::MT19937::uniform(0, -addr2data_delay) : addr2data_delay;
157  if(resp_delay) {
158  rchannel_pending_rsp.push_back({state.pending_tx, resp_delay - 1});
159  } else
160  rchannel_pending_rsp.push_back({state.pending_tx, 0});
161  }
162  state.last_phase = tlm::BEGIN_RESP;
163  return tlm::TLM_ACCEPTED;
164  }
165  default:
166  SCCWARN(SCMOD) << phase << " is unsupported phase transaction combination";
167  return tlm::TLM_ACCEPTED;
168  }
169 }
170 
171 template <unsigned int DATA_WIDTH, unsigned int ADDR_WIDTH, unsigned int ID_WIDTH, unsigned int USER_WIDTH>
172 inline void target<DATA_WIDTH, ADDR_WIDTH, ID_WIDTH, USER_WIDTH>::achannel_req_t() {
173  wait(SC_ZERO_TIME);
174  wait(clk_i.posedge_event());
175  while(true) {
176  while(resetn_i.read() == false)
177  wait(clk_i.posedge_event());
178  while(resetn_i.read() == true) {
179  gnt_o.write(req2gnt_delay == 0);
180  do {
181  wait(this->req_i.posedge_event() | clk_delayed);
182  } while(this->req_i.read() == false);
183  auto data_len = DATA_WIDTH / 8;
185  gp->set_streaming_width(data_len);
186  gp->set_address(addr_i.read());
187  gp->set_command(we_i.read() ? tlm::TLM_WRITE_COMMAND : tlm::TLM_READ_COMMAND);
188  auto be = static_cast<unsigned>(be_i.read());
189  auto cnt = util::bit_count(be);
190  gp->set_streaming_width(cnt);
191  gp->set_data_length(cnt);
192  if(we_i.read()) {
193  auto bus_data = wdata_i.read();
194  auto tx_byte_idx = 0;
195  for(auto i = 0U, be_idx = 0U; i < DATA_WIDTH; i += 8, ++be_idx) {
196  if(be & (1U << be_idx)) {
197  *(gp->get_data_ptr() + tx_byte_idx) = bus_data.range(i + 7, i).to_uint();
198  tx_byte_idx++;
199  }
200  }
201  SCCTRACE(SCMOD) << "Got write request to address 0x" << std::hex << gp->get_address();
202  } else
203  SCCTRACE(SCMOD) << "Got read request to address 0x" << std::hex << gp->get_address();
204  auto ext = gp->get_extension<obi::obi_extension>();
205  if(ID_WIDTH && aid_i.get_interface())
206  ext->set_id(aid_i->read());
207  if(USER_WIDTH) {
208  if(auser_i.get_interface())
209  ext->set_auser(auser_i->read());
210  if(wuser_i.get_interface() && we_i.read())
211  ext->set_duser(wuser_i->read());
212  }
213  auto& state = states[gp.get()];
214  phase_type phase = tlm::BEGIN_REQ;
215  auto delay = sc_core::SC_ZERO_TIME;
216  auto ret = isckt->nb_transport_fw(*gp, phase, delay);
217  auto startResp = false;
218  state.last_phase = phase;
219  if(ret == tlm::TLM_ACCEPTED) {
220  SCCTRACE(SCMOD) << "waiting for TLM reponse";
221  auto resp = achannel_rsp.get();
222  sc_assert(resp.get() == gp.get());
223  }
224  unsigned gnt_delay = req2gnt_delay < 0 ? scc::MT19937::uniform(0, -req2gnt_delay) : req2gnt_delay;
225  for(unsigned i = 0U; i < gnt_delay; ++i)
226  wait(clk_i.posedge_event());
227  gnt_o.write(true);
228  wait(clk_i.posedge_event());
229  state.addrPhaseFinished = true;
230  if(state.last_phase == tlm::BEGIN_RESP) {
231  unsigned resp_delay = addr2data_delay < 0 ? scc::MT19937::uniform(0, -addr2data_delay) : addr2data_delay;
232  if(resp_delay) {
233  rchannel_pending_rsp.push_back({gp, resp_delay - 1});
234  } else
235  rchannel_rsp.notify(gp);
236  } else {
237  state.pending_tx = gp;
238  }
239  }
240  }
241 }
242 
243 template <unsigned int DATA_WIDTH, unsigned int ADDR_WIDTH, unsigned int ID_WIDTH, unsigned int USER_WIDTH>
244 inline void target<DATA_WIDTH, ADDR_WIDTH, ID_WIDTH, USER_WIDTH>::rchannel_rsp_t() {
245  rdata_o.write(0);
246  err_o.write(false);
247  if(ID_WIDTH && r_id_o.get_interface())
248  r_id_o->write(0);
249  if(USER_WIDTH && ruser_o.get_interface())
250  ruser_o->write(0);
251  while(true) {
252  rvalid_o.write(false);
253  tlm::scc::tlm_gp_shared_ptr tx = rchannel_rsp.get();
254  if(tx->get_command() == tlm::TLM_READ_COMMAND) {
255  auto offset = tx->get_address() % (DATA_WIDTH / 8);
256  auto rx_data{sc_dt::sc_uint<DATA_WIDTH>(0)};
257  for(unsigned i = 0U, j = offset * 8; i < tx->get_data_length(); ++i, j += 8) {
258  rx_data.range(j + 7, j) = *(tx->get_data_ptr() + i);
259  }
260  rdata_o.write(rx_data);
261  SCCTRACE(SCMOD) << "responding to read request to address 0x" << std::hex << tx->get_address();
262  } else
263  SCCTRACE(SCMOD) << "responding to write request to address 0x" << std::hex << tx->get_address();
264  err_o.write(tx->get_response_status() != tlm::TLM_OK_RESPONSE);
265  if(ID_WIDTH || USER_WIDTH) {
266  auto ext = tx->get_extension<obi::obi_extension>();
267  if(ID_WIDTH && r_id_o.get_interface())
268  r_id_o->write(ext->get_id());
269  if(USER_WIDTH && ruser_o.get_interface())
270  ruser_o->write(ext->get_duser());
271  }
272  rvalid_o.write(true);
273  wait(sample_delay); // let rready settle
274  while(!rready_i.read())
275  wait(rready_i.value_changed_event());
276  phase_type phase = tlm::END_RESP;
277  auto delay = sc_core::SC_ZERO_TIME;
278  auto ret = isckt->nb_transport_fw(*tx, phase, delay);
279  auto it = states.find(tx.get());
280  sc_assert(it != states.end());
281  states.erase(it);
282  wait(clk_i.posedge_event());
283  }
284 }
285 } // namespace pin
286 } // namespace obi
static uint64_t uniform()
Definition: mt19937_rng.h:60
initiator socket mixin
void register_nb_transport_bw(std::function< sync_enum_type(transaction_type &, phase_type &, sc_core::sc_time &)> cb)
payload_type * allocate()
get a plain tlm_payload_type without extensions
Definition: tlm_mm.h:185
T * get() const noexcept
Return the stored pointer.
Definition: tlm_gp_shared.h:91
TLM2.0 components modeling OBI.
Definition: obi_tlm.h:25
priority event queue
Definition: peq.h:41
static tlm_mm & get()
accessor function of the singleton
Definition: tlm_mm.h:293