scc  2024.06
SystemC components library
initiator.h
1 /*******************************************************************************
2  * Copyright 2019-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_APB_PIN_INITIATOR_H_
18 #define _BUS_APB_PIN_INITIATOR_H_
19 
20 #ifndef SC_INCLUDE_DYNAMIC_PROCESSES
21 #define SC_INCLUDE_DYNAMIC_PROCESSES
22 #endif
23 
24 #include <interfaces/apb/apb_tlm.h>
25 #include <scc/report.h>
26 #include <scc/signal_opt_ports.h>
27 #include <scc/utilities.h>
28 #include <tlm/scc/target_mixin.h>
29 #include <tlm>
30 #include <tlm_utils/peq_with_get.h>
31 #include <type_traits>
32 
33 namespace apb {
34 namespace pin {
35 
36 template <unsigned DATA_WIDTH, unsigned ADDR_WIDTH = 32> class initiator : sc_core::sc_module {
37  static constexpr bool is_larger(unsigned x) { return x > 64U; }
38 
39 public:
40  using addr_t = sc_dt::sc_uint<ADDR_WIDTH>;
41  using data_t = typename std::conditional<is_larger(DATA_WIDTH), sc_dt::sc_biguint<DATA_WIDTH>, sc_dt::sc_uint<DATA_WIDTH>>::type;
42  using strb_t = sc_dt::sc_uint<DATA_WIDTH / 8>;
43 
44  sc_core::sc_in<bool> PCLK_i{"PCLK_i"};
45  sc_core::sc_in<bool> PRESETn_i{"PRESETn_i"};
46  sc_core::sc_out<addr_t> PADDR_o{"PADDR_o"};
47  scc::sc_out_opt<sc_dt::sc_uint<3>> PPROT_o{"PPROT_o"};
48  scc::sc_out_opt<bool> PNSE_o{"PNSE_o"};
49  sc_core::sc_out<bool> PSELx_o{"PSELx_o"};
50  sc_core::sc_out<bool> PENABLE_o{"PENABLE_o"};
51  sc_core::sc_out<bool> PWRITE_o{"PWRITE_o"};
52  sc_core::sc_out<data_t> PWDATA_o{"PWDATA_o"};
53  scc::sc_out_opt<strb_t> PSTRB_o{"PSTRB_o"};
54  sc_core::sc_in<bool> PREADY_i{"PREADY_i"};
55  sc_core::sc_in<data_t> PRDATA_i{"PRDATA_i"};
56  sc_core::sc_in<bool> PSLVERR_i{"PSLVERR_i"};
57  scc::sc_out_opt<bool> PWAKEUP_o{"PWAKEUP_o"};
58 
60 
61  initiator(const sc_core::sc_module_name& nm);
62  virtual ~initiator() = default;
63 
64 private:
65  void bus_task();
66 
67  tlm_utils::peq_with_get<tlm::tlm_generic_payload> inqueue{"inqueue"};
68 };
69 
71 // implementations of functions
73 template <unsigned DATA_WIDTH, unsigned ADDR_WIDTH>
74 initiator<DATA_WIDTH, ADDR_WIDTH>::initiator(const sc_core::sc_module_name& nm)
75 : sc_module(nm) {
76  SC_HAS_PROCESS(initiator);
77  SC_THREAD(bus_task);
78 
80  [this](tlm::tlm_generic_payload& payload, tlm::tlm_phase& phase, sc_core::sc_time& delay) -> tlm::tlm_sync_enum {
81  if(phase == tlm::BEGIN_REQ) {
82  if(payload.has_mm())
83  payload.acquire();
84  this->inqueue.notify(payload);
85  }
86  return tlm::TLM_ACCEPTED;
87  });
88 }
89 
90 template <unsigned DATA_WIDTH, unsigned ADDR_WIDTH> inline void initiator<DATA_WIDTH, ADDR_WIDTH>::bus_task() {
91  auto& hready = PREADY_i.read();
92  while(true) {
93  wait(inqueue.get_event());
94  while(auto trans = inqueue.get_next_transaction()) {
95  auto addr_offset = trans->get_address() & (DATA_WIDTH / 8 - 1);
96  auto upper = addr_offset + trans->get_data_length();
97  if(!PSTRB_o.get_interface() && addr_offset && upper != (DATA_WIDTH / 8)) {
98  SCCERR(SCMOD) << "Narrow accesses are not supported as there is no PSTRB signal! Skipping " << *trans;
99  tlm::tlm_phase phase{tlm::END_RESP};
100  sc_core::sc_time delay;
101  trans->set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
102  tsckt->nb_transport_bw(*trans, phase, delay);
103  } else if(upper > DATA_WIDTH / 8) {
104  SCCERR(SCMOD) << "Illegal length of payload since it would cause a wrap-around! Skipping " << *trans;
105  tlm::tlm_phase phase{tlm::END_RESP};
106  sc_core::sc_time delay;
107  trans->set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
108  tsckt->nb_transport_bw(*trans, phase, delay);
109  } else {
110  SCCDEBUG(SCMOD) << "Recv beg req for read to addr 0x" << std::hex << trans->get_address() << ", starting APB setup phase, ";
111  auto bytes_exp = scc::ilog2(trans->get_data_length());
112  auto width_exp = scc::ilog2(DATA_WIDTH / 8);
113  size_t size = 0;
114  for(; size < bytes_exp; ++size)
115  if(trans->get_address() & (1 << size))
116  break; // i contains the first bit not being 0
117  auto* ext = trans->template get_extension<apb_extension>();
118  if(trans->is_write()) {
119  if(upper <= DATA_WIDTH / 8) {
120  data_t data{0};
121  strb_t strb{0};
122  for(size_t i = 0; i < upper; ++i) {
123  if(i >= addr_offset) {
124  data.range(i * 8 + 7, i * 8) = *(trans->get_data_ptr() + i - addr_offset);
125  strb[i] = 1;
126  }
127  }
128  PWDATA_o.write(data);
129  if(PSTRB_o.get_interface())
130  PSTRB_o.write(strb);
131  }
132  }
133  PWRITE_o.write(trans->is_write());
134  PADDR_o.write(trans->get_address() - addr_offset); // adjust address to be aligned
135  PSELx_o.write(true);
136  if(PPROT_o.get_interface() && ext)
137  PPROT_o.write(ext ? ext->get_protection() : 0);
138  if(PNSE_o.get_interface())
139  PNSE_o.write(ext ? ext->is_nse() : false);
140  wait(PCLK_i.posedge_event());
141  SCCDEBUG(SCMOD) << "APB setup phase finished, sending end req for access to addr 0x" << std::hex << trans->get_address();
142  tlm::tlm_phase phase{tlm::END_REQ};
143  sc_core::sc_time delay;
144  auto res = tsckt->nb_transport_bw(*trans, phase, delay);
145  SCCDEBUG(SCMOD) << "Starting APB access phase";
146  PENABLE_o.write(true);
147  wait(1_ps);
148  while(!PREADY_i.read())
149  wait(PREADY_i.value_changed_event());
150  wait(PCLK_i.posedge_event());
151  if(trans->is_read()) {
152  auto data = PRDATA_i.read();
153  for(size_t j = addr_offset, i = 0; i < trans->get_data_length(); ++j, ++i)
154  *(trans->get_data_ptr() + i) = data(8 * j + 7, 8 * j).to_uint();
155  }
156  trans->set_response_status(PSLVERR_i.read() ? tlm::TLM_GENERIC_ERROR_RESPONSE : tlm::TLM_OK_RESPONSE);
157  phase = tlm::BEGIN_RESP;
158  delay = sc_core::SC_ZERO_TIME;
159  SCCDEBUG(SCMOD) << "Sending beg resp for access to addr 0x" << std::hex << trans->get_address();
160  res = tsckt->nb_transport_bw(*trans, phase, delay);
161  SCCDEBUG(SCMOD) << "APB access phase finished";
162  PENABLE_o.write(false);
163  PSELx_o.write(false);
164  }
165  }
166  }
167 }
168 } // namespace pin
169 } // namespace apb
170 
171 #endif /* _BUS_APB_PIN_INITIATOR_H_ */
initiator ID recording TLM extension
void register_nb_transport_fw(std::function< sync_enum_type(transaction_type &, phase_type &, sc_core::sc_time &)> cb)
Definition: target_mixin.h:78
TLM2.0 components modeling APB.
Definition: apb_tlm.cpp:21