scc 2025.09
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 <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
33namespace apb {
34namespace pin {
35
36template <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
39public:
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
64private:
65 void bus_task();
66
67 tlm_utils::peq_with_get<tlm::tlm_generic_payload> inqueue{"inqueue"};
68};
69
71// implementations of functions
73template <unsigned DATA_WIDTH, unsigned ADDR_WIDTH>
74initiator<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
90template <unsigned DATA_WIDTH, unsigned ADDR_WIDTH> inline void initiator<DATA_WIDTH, ADDR_WIDTH>::bus_task() {
91 while(true) {
92 wait(inqueue.get_event());
93 while(auto trans = inqueue.get_next_transaction()) {
94 auto addr_offset = trans->get_address() & (DATA_WIDTH / 8 - 1);
95 auto upper = addr_offset + trans->get_data_length();
96 if(!PSTRB_o.get_interface() && (addr_offset || upper != (DATA_WIDTH / 8))) {
97 SCCERR(SCMOD) << "Narrow accesses are not supported before APB4 as there is no PSTRB signal! Skipping " << *trans;
98 tlm::tlm_phase phase{tlm::END_RESP};
99 sc_core::sc_time delay;
100 trans->set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
101 tsckt->nb_transport_bw(*trans, phase, delay);
102 } else if(upper > DATA_WIDTH / 8) {
103 SCCERR(SCMOD) << "Illegal length of payload since it would cause a wrap-around! Skipping " << *trans;
104 tlm::tlm_phase phase{tlm::END_RESP};
105 sc_core::sc_time delay;
106 trans->set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
107 tsckt->nb_transport_bw(*trans, phase, delay);
108 } else {
109 SCCDEBUG(SCMOD) << "Recv beg req for read to addr 0x" << std::hex << trans->get_address() << ", starting APB setup phase, ";
110 auto* ext = trans->template get_extension<apb_extension>();
111 if(trans->is_write()) {
112 data_t data{0};
113 strb_t strb{0};
114 // Handle TLM byte enables if present
115 if(trans->get_byte_enable_ptr() && trans->get_byte_enable_length() > 0) {
116 for(size_t i = 0; i < DATA_WIDTH / 8; ++i) {
117 if(i >= addr_offset && i < upper) {
118 auto be_idx = (i - addr_offset) % trans->get_byte_enable_length();
119 if(trans->get_byte_enable_ptr()[be_idx] != 0) {
120 data.range(i * 8 + 7, i * 8) = *(trans->get_data_ptr() + i - addr_offset);
121 strb[i] = 1;
122 }
123 }
124 }
125 } else {
126 // No byte enables, write contiguous data
127 for(size_t i = 0; i < upper; ++i) {
128 if(i >= addr_offset) {
129 data.range(i * 8 + 7, i * 8) = *(trans->get_data_ptr() + i - addr_offset);
130 strb[i] = 1;
131 }
132 }
133 }
134 PWDATA_o.write(data);
135 if(PSTRB_o.get_interface())
136 PSTRB_o.write(strb);
137 } else if(PSTRB_o.get_interface()) {
138 // From spec : For read transfers the bus master must drive all bits of PSTRB LOW
139 PSTRB_o.write(0);
140 }
141 PWRITE_o.write(trans->is_write());
142 PADDR_o.write(trans->get_address() - addr_offset); // adjust address to be aligned
143 PSELx_o.write(true);
144 if(PPROT_o.get_interface())
145 PPROT_o.write(ext ? ext->get_protection() : false);
146 if(PNSE_o.get_interface())
147 PNSE_o.write(ext ? ext->is_nse() : false);
148 wait(PCLK_i.posedge_event());
149 SCCDEBUG(SCMOD) << "APB setup phase finished, sending end req for access to addr 0x" << std::hex << trans->get_address();
150 tlm::tlm_phase phase{tlm::END_REQ};
151 sc_core::sc_time delay;
152 tsckt->nb_transport_bw(*trans, phase, delay);
153 SCCDEBUG(SCMOD) << "Starting APB access phase";
154 PENABLE_o.write(true);
155 while(!PREADY_i.read())
156 wait(PREADY_i.value_changed_event());
157 wait(PCLK_i.posedge_event());
158 if(trans->is_read()) {
159 auto data = PRDATA_i.read();
160 for(size_t j = addr_offset, i = 0; i < trans->get_data_length(); ++j, ++i)
161 *(trans->get_data_ptr() + i) = data(8 * j + 7, 8 * j).to_uint();
162 }
163 trans->set_response_status(PSLVERR_i.read() ? tlm::TLM_GENERIC_ERROR_RESPONSE : tlm::TLM_OK_RESPONSE);
164 phase = tlm::BEGIN_RESP;
165 delay = sc_core::SC_ZERO_TIME;
166 SCCDEBUG(SCMOD) << "Sending beg resp for access to addr 0x" << std::hex << trans->get_address();
167 tsckt->nb_transport_bw(*trans, phase, delay);
168 SCCDEBUG(SCMOD) << "APB access phase finished";
169 PENABLE_o.write(false);
170 PSELx_o.write(false);
171 }
172 if(trans->has_mm())
173 trans->release();
174 }
175 }
176}
177} // namespace pin
178} // namespace apb
179
180#endif /* _BUS_APB_PIN_INITIATOR_H_ */
A template class for an optional input port with optimized binding.
void register_nb_transport_fw(std::function< sync_enum_type(transaction_type &, phase_type &, sc_core::sc_time &)> cb)
pin level adapters
Definition initiator.h:34
TLM2.0 components modeling APB.
Definition apb_tlm.cpp:21