scc 2025.09
SystemC components library
axi_initiator.cpp
1/*
2 * Copyright 2021 Arteris IP
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#ifndef SC_INCLUDE_DYNAMIC_PROCESSES
17#define SC_INCLUDE_DYNAMIC_PROCESSES
18#endif
19#include <atp/timing_params.h>
20#include <axi/axi_tlm.h>
21#include <axi/pe/axi_initiator.h>
22#include <scc/report.h>
23#include <tlm/scc/tlm_gp_shared.h>
24
25using namespace sc_core;
26using sem_lock = scc::ordered_semaphore::lock;
27
28namespace axi {
29namespace pe {
30
31namespace {
32uint8_t log2n(uint8_t siz) { return ((siz > 1) ? 1 + log2n(siz >> 1) : 0); }
33
34} // anonymous namespace
35
36#if SYSTEMC_VERSION < 20250221
37SC_HAS_PROCESS(axi_initiator_b);
38#endif
39axi_initiator_b::axi_initiator_b(sc_core::sc_module_name nm, sc_core::sc_port_b<axi::axi_fw_transport_if<axi_protocol_types>>& port,
40 size_t transfer_width, flavor_e flavor)
41: sc_module(nm)
42, socket_fw(port)
43, transfer_width_in_bytes(transfer_width / 8)
44, flavor(flavor) {
45 fw_i.bind(*this);
46 SC_METHOD(clk_counter);
47 sensitive << clk_i.pos();
48
49 if(flavor == flavor_e::AXI)
50 for(auto i = 0u; i < 16; i++)
51 sc_core::sc_spawn([this]() { snoop_thread(); });
52}
53
54axi_initiator_b::~axi_initiator_b() {
55 for(auto& e : tx_state_by_tx)
56 delete e.second;
57}
58
59void axi_initiator_b::end_of_elaboration() {
60 clk_if = dynamic_cast<sc_core::sc_clock*>(clk_i.get_interface());
61 for(auto i = 0U; i < outstanding_snoops.get_value(); ++i) {
62 sc_spawn(sc_bind(&axi_initiator_b::snoop_thread, this));
63 }
64}
65
66void axi_initiator_b::b_snoop(payload_type& trans, sc_core::sc_time& t) {
67 if(bw_o.get_interface()) {
68 auto latency = bw_o->transport(trans);
69 if(latency < std::numeric_limits<unsigned>::max())
70 t += latency * clk_period;
71 }
72}
73
74tlm::tlm_sync_enum axi_initiator_b::nb_transport_bw(payload_type& trans, phase_type& phase, sc_core::sc_time& t) {
75 SCCTRACE(SCMOD) << __FUNCTION__ << " received with phase " << phase << " with delay = " << t << " with trans " << trans;
76 if(phase == tlm::BEGIN_REQ) { // snoop
77 snp_peq.notify(trans, t);
78 } else if(phase == END_PARTIAL_RESP || phase == tlm::END_RESP) { // snoop
79 auto it = snp_state_by_id.find(&trans);
80 sc_assert(it != snp_state_by_id.end());
81 it->second->peq.notify(std::make_tuple(&trans, phase), t);
82 } else { // read/write
83 auto it = tx_state_by_tx.find(&trans);
84 sc_assert(it != tx_state_by_tx.end());
85 auto txs = it->second;
86 txs->peq.notify(std::make_tuple(&trans, phase), t);
87 }
88 return tlm::TLM_ACCEPTED;
89}
90
91void axi_initiator_b::invalidate_direct_mem_ptr(sc_dt::uint64 start_range, sc_dt::uint64 end_range) {}
92
93tlm::tlm_phase axi_initiator_b::send(payload_type& trans, axi_initiator_b::tx_state* txs, tlm::tlm_phase phase) {
94 sc_core::sc_time delay;
95 SCCTRACE(SCMOD) << "Send " << phase << " of " << trans;
96 tlm::tlm_sync_enum ret = socket_fw->nb_transport_fw(trans, phase, delay);
97 if(ret == tlm::TLM_UPDATED) {
98 wait(delay);
99 SCCTRACE(SCMOD) << "Received " << phase << " for " << trans;
100 return phase;
101 } else {
102 auto waiting = txs->peq.has_next();
103 auto entry = txs->peq.get();
104 if(waiting)
105 SCCFATAL(SCMOD) << "there is a waiting " << std::get<0>(entry) << " with phase " << std::get<1>(entry);
106 sc_assert(!txs->peq.has_next());
107 sc_assert(std::get<0>(entry) == &trans);
108 SCCTRACE(SCMOD) << "in send() Received " << std::get<1>(entry) << " for " << trans;
109 return std::get<1>(entry);
110 }
111}
112
113void axi_initiator_b::transport(payload_type& trans, bool blocking) {
114 auto axi_id = get_axi_id(trans);
115 if(flavor == flavor_e::AXI) {
116 if(!trans.get_extension<axi::axi4_extension>() && !trans.get_extension<axi::axi3_extension>()) {
117 auto ace = trans.set_extension<axi::ace_extension>(nullptr);
118 sc_assert(ace && "No valid extension found in transaction");
119 auto axi4 = new axi::axi4_extension();
120 *static_cast<axi::axi4*>(axi4) = *static_cast<axi::axi4*>(ace);
121 *static_cast<axi::common*>(axi4) = *static_cast<axi::common*>(ace);
122 trans.set_extension(axi4);
123 delete ace;
124 }
125 } else {
126 sc_assert(trans.get_extension<axi::ace_extension>() && "No ACE extension found in transaction");
127 }
128 SCCTRACE(SCMOD) << "got transport req for " << trans;
129 if(blocking) {
130 sc_time t;
131 socket_fw->b_transport(trans, t);
132 } else {
133 auto it = tx_state_by_tx.find(&trans);
134 if(it == tx_state_by_tx.end()) {
135 bool success;
136 std::tie(it, success) = tx_state_by_tx.insert(std::make_pair(&trans, new tx_state()));
137 }
138 if(trans.is_read())
139 rd_waiting++;
140 else
141 wr_waiting++;
142 auto& txs = it->second;
143 auto timing_e = trans.set_auto_extension<atp::timing_params>(nullptr);
144
145 if(enable_id_serializing.get_value()) {
146 if(!id_mtx[axi_id]) {
147 id_mtx[axi_id] = new scc::ordered_semaphore(1);
148 }
149 id_mtx[axi_id]->wait(); // wait until running tx with same id is over
150 }
151 txs->active_tx = &trans;
152 auto burst_length = 0;
153 if(auto e = trans.get_extension<axi::ace_extension>()) {
154 burst_length = is_dataless(e) ? 1 : e->get_length() + 1;
155 } else if(auto e = trans.get_extension<axi::axi4_extension>()) {
156 burst_length = e->get_length() + 1;
157 } else if(auto e = trans.get_extension<axi::axi3_extension>()) {
158 burst_length = e->get_length() + 1;
159 }
160 SCCTRACE(SCMOD) << "start transport " << trans;
161 tlm::tlm_phase next_phase{tlm::UNINITIALIZED_PHASE};
162 if(!trans.is_read()) { // data less via write channel
163 if(!data_interleaving.get_value()) { // Note that AXI4 does not allow write data interleaving, and ncore3 only supports AXI4.
164 sem_lock lck(wr_chnl);
165 wr_waiting--;
166 wr_outstanding++;
168 for(unsigned i = 1; i < (timing_e ? timing_e->awtv : awtv.get_value()); ++i) {
169 wait(clk_i.posedge_event());
170 }
171 SCCTRACE(SCMOD) << "starting " << burst_length << " write beats of " << trans;
172 for(unsigned i = 0; i < burst_length - 1; ++i) {
173 if(protocol_cb[axi::fsm::BegPartReqE])
174 protocol_cb[axi::fsm::BegPartReqE](trans, false);
175 auto res = send(trans, txs, axi::BEGIN_PARTIAL_REQ);
176 if(axi::END_PARTIAL_REQ != res)
177 SCCFATAL(SCMOD) << "target responded with " << res << " for the " << i << "th beat of " << burst_length
178 << " beats in transaction " << trans;
179 for(unsigned i = 0; i < (timing_e ? timing_e->wbv : wbv.get_value()); ++i)
180 wait(clk_i.posedge_event());
181 if(protocol_cb[axi::fsm::EndPartReqE])
182 protocol_cb[axi::fsm::EndPartReqE](trans, false);
183 }
184 auto res = send(trans, txs, tlm::BEGIN_REQ);
185 if(res == axi::BEGIN_PARTIAL_RESP || res == tlm::BEGIN_RESP)
186 next_phase = res;
187 else if(res != tlm::END_REQ)
188 SCCERR(SCMOD) << "target did not repsond with END_REQ to a BEGIN_REQ";
189 wait(clk_i.posedge_event());
190 } else { // AXI3 allows data interleaving and there may be support for AXI3 in Symphony
191 SCCTRACE(SCMOD) << "starting " << burst_length << " write beats of " << trans;
192 for(unsigned i = 0; i < burst_length - 1; ++i) {
193 sem_lock lck(wr_chnl);
194 if(i == 0) {
195 wr_waiting--;
196 wr_outstanding++;
198 for(unsigned i = 1; i < (timing_e ? timing_e->awtv : awtv.get_value()); ++i)
199 wait(clk_i.posedge_event());
200 }
201 auto res = send(trans, txs, axi::BEGIN_PARTIAL_REQ);
202 sc_assert(axi::END_PARTIAL_REQ == res);
203 for(unsigned i = 1; i < (timing_e ? timing_e->wbv : wbv.get_value()); ++i)
204 wait(clk_i.posedge_event());
205 }
206 sem_lock lck(wr_chnl);
207 if(burst_length == 1) {
208 wr_waiting--;
209 wr_outstanding++;
210 }
211 if(protocol_cb[axi::fsm::BegReqE])
212 protocol_cb[axi::fsm::BegReqE](trans, false);
213 auto res = send(trans, txs, tlm::BEGIN_REQ);
214 if(res == axi::BEGIN_PARTIAL_RESP || res == tlm::BEGIN_RESP)
215 next_phase = res;
216 else if(res != tlm::END_REQ)
217 SCCERR(SCMOD) << "target did not repsond with END_REQ to a BEGIN_REQ";
218 wait(clk_i.posedge_event());
219 if(protocol_cb[axi::fsm::EndReqE])
220 protocol_cb[axi::fsm::EndReqE](trans, false);
221 }
222 } else {
223 sem_lock lck(rd_chnl);
224 rd_waiting--;
225 rd_outstanding++;
227 for(unsigned i = 1; i < (timing_e ? timing_e->artv : artv.get_value()); ++i)
228 wait(clk_i.posedge_event());
229 SCCTRACE(SCMOD) << "starting address phase of " << trans;
230 if(protocol_cb[axi::fsm::BegPartReqE])
231 protocol_cb[axi::fsm::BegPartReqE](trans, false);
232 auto res = send(trans, txs, tlm::BEGIN_REQ);
233 if(res == axi::BEGIN_PARTIAL_RESP || res == tlm::BEGIN_RESP)
234 next_phase = res;
235 else if(res != tlm::END_REQ)
236 SCCERR(SCMOD) << "target did not repsond with END_REQ to a BEGIN_REQ";
237 wait(clk_i.posedge_event());
238 if(protocol_cb[axi::fsm::EndReqE])
239 protocol_cb[axi::fsm::EndReqE](trans, false);
240 }
241 auto finished = false;
242 if(!trans.is_read() || !trans.get_data_length())
243 burst_length = 1;
244 const auto exp_burst_length = burst_length;
245 do {
246 // waiting for response
247 auto entry = next_phase == tlm::UNINITIALIZED_PHASE ? txs->peq.get() : std::make_tuple(&trans, next_phase);
248 next_phase = tlm::UNINITIALIZED_PHASE;
249 // Handle optional CRESP response
250 if(std::get<0>(entry) == &trans && std::get<1>(entry) == tlm::BEGIN_RESP) {
251 if(protocol_cb[axi::fsm::BegRespE])
252 protocol_cb[axi::fsm::BegRespE](trans, false);
253 SCCTRACE(SCMOD) << "received last beat of " << trans;
254 auto delay_in_cycles = timing_e ? (trans.is_read() ? timing_e->rbr : timing_e->br) : br.get_value();
255 for(unsigned i = 0; i < delay_in_cycles; ++i)
256 wait(clk_i.posedge_event());
257 burst_length--;
258 tlm::tlm_phase phase = tlm::END_RESP;
259 sc_time delay = clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME;
260 socket_fw->nb_transport_fw(trans, phase, delay);
261 if(burst_length)
262 SCCWARN(SCMOD) << "got wrong number of burst beats, expected " << exp_burst_length << ", got "
263 << exp_burst_length - burst_length;
264 wait(clk_i.posedge_event());
265 if(protocol_cb[axi::fsm::EndRespE])
266 protocol_cb[axi::fsm::EndRespE](trans, false);
267 finished = true;
268 } else if(std::get<0>(entry) == &trans && std::get<1>(entry) == axi::BEGIN_PARTIAL_RESP) { // RDAT without CRESP case
269 SCCTRACE(SCMOD) << "received beat = " << burst_length << " with trans " << trans;
270 auto delay_in_cycles = timing_e ? timing_e->rbr : rbr.get_value();
271 for(unsigned i = 0; i < delay_in_cycles; ++i)
272 wait(clk_i.posedge_event());
273 burst_length--;
274 if(protocol_cb[axi::fsm::BegPartRespE])
275 protocol_cb[axi::fsm::BegPartRespE](trans, false);
276 tlm::tlm_phase phase = axi::END_PARTIAL_RESP;
277 sc_time delay = clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME;
278 auto res = socket_fw->nb_transport_fw(trans, phase, delay);
279 if(res == tlm::TLM_UPDATED) {
280 next_phase = phase;
281 wait(delay);
282 }
283 if(protocol_cb[axi::fsm::EndPartRespE])
284 protocol_cb[axi::fsm::EndPartRespE](trans, false);
285 }
286 } while(!finished);
287 if(flavor == flavor_e::ACE) {
288 if(trans.is_read() && rla.get_value() != std::numeric_limits<unsigned>::max()) {
289 for(unsigned i = 0; i < rla.get_value(); ++i)
290 wait(clk_i.posedge_event());
291 tlm::tlm_phase phase = axi::ACK;
292 sc_time delay = SC_ZERO_TIME;
293 socket_fw->nb_transport_fw(trans, phase, delay);
294 wait(clk_i.posedge_event());
295
296 } else if(trans.is_write() && ba.get_value() != std::numeric_limits<unsigned>::max()) {
297 for(unsigned i = 0; i < ba.get_value(); ++i)
298 wait(clk_i.posedge_event());
299 tlm::tlm_phase phase = axi::ACK;
300 sc_time delay = SC_ZERO_TIME;
301 socket_fw->nb_transport_fw(trans, phase, delay);
302 wait(clk_i.posedge_event());
303 }
304 }
305 if(trans.is_read())
306 rd_outstanding--;
307 else
308 wr_outstanding--;
309 SCCTRACE(SCMOD) << "finished non-blocking protocol";
310 if(enable_id_serializing.get_value()) {
311 id_mtx[axi_id]->post();
312 }
313 txs->active_tx = nullptr;
314 any_tx_finished.notify(SC_ZERO_TIME);
315 }
316 SCCTRACE(SCMOD) << "finished transport req for " << trans;
317}
318
319// This process handles the SNOOP request received
320void axi_initiator_b::snoop_thread() {
321 tlm::scc::tlm_gp_shared_ptr trans{nullptr};
322 while(true) {
323 while(!(trans = snp_peq.get_next_transaction())) {
324 wait(snp_peq.get_event());
325 }
326 snoops_in_flight++;
327 SCCDEBUG(SCMOD) << "start snoop #" << snoops_in_flight;
328 auto req_ext = trans->get_extension<ace_extension>();
329 sc_assert(req_ext != nullptr);
330
331 auto it = snp_state_by_id.find(&trans);
332 if(it == snp_state_by_id.end()) {
333 bool success;
334 std::tie(it, success) = snp_state_by_id.insert(std::make_pair(trans.get(), new tx_state()));
335 }
336
337 sc_time delay = clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME;
338 tlm::tlm_phase phase = tlm::END_REQ;
339 // here delay is not used in nb_fw of following module
340 // therefore one cycle delay between BEG_REQ and END_REQ should be explicitly called here??
341 if(protocol_cb[axi::fsm::BegReqE])
342 protocol_cb[axi::fsm::BegReqE](*trans, true);
343 socket_fw->nb_transport_fw(*trans, phase, delay);
344 auto cycles = 0U;
345 if(bw_o.get_interface())
346 cycles = bw_o->transport(*trans);
347 if(protocol_cb[axi::fsm::EndReqE])
348 protocol_cb[axi::fsm::EndReqE](*trans, true);
349 if(cycles < std::numeric_limits<unsigned>::max()) {
350 // we handle the snoop access ourselfs
351 for(size_t i = 0; i <= cycles; ++i)
352 wait(clk_i.posedge_event());
353 snoop_resp(*trans);
354 }
355 // finish snoop response, should release tlm gp_shared_ptr
356 SCCTRACE(SCMOD) << " finish snoop response, release gp_shared_ptr";
357 snoops_in_flight--;
358 trans = nullptr;
359 }
360}
361
362void axi_initiator_b::snoop_resp(payload_type& trans, bool sync) {
363 auto it = snp_state_by_id.find(&trans);
364 sc_assert(it != snp_state_by_id.end());
365 auto& txs = it->second;
366 auto data_len = trans.get_data_length();
367 auto burst_length = data_len / transfer_width_in_bytes;
368 if(burst_length < 1)
369 burst_length = 1;
370 tlm::tlm_phase next_phase{tlm::UNINITIALIZED_PHASE};
371 auto delay_in_cycles = wbv.get_value();
372 sem_lock lck(sresp_chnl);
373 /*
374 * here according to spec, ccresp should first be checked to see whether there is data transfer( decided by TC)
375 * if there is data to transfer, start cache data transfer, otherwise only crresp
376 * */
377 SCCTRACE(SCMOD) << "starting snoop resp with " << burst_length << " beats of " << trans;
378 for(unsigned i = 0; i < burst_length - 1; ++i) {
379 if(protocol_cb[axi::fsm::BegPartRespE])
380 protocol_cb[axi::fsm::BegPartRespE](trans, true);
381 auto res = send(trans, txs, axi::BEGIN_PARTIAL_RESP);
382 sc_assert(axi::END_PARTIAL_RESP == res);
383 wait(clk_i.posedge_event());
384 if(protocol_cb[axi::fsm::EndPartRespE])
385 protocol_cb[axi::fsm::EndPartRespE](trans, true);
386 for(unsigned i = 1; i < delay_in_cycles; ++i)
387 wait(clk_i.posedge_event());
388 }
389 if(protocol_cb[axi::fsm::BegRespE])
390 protocol_cb[axi::fsm::BegRespE](trans, true);
391 auto res = send(trans, txs, tlm::BEGIN_RESP);
392 if(res != tlm::END_RESP)
393 SCCERR(SCMOD) << "target did not respond with END_RESP to a BEGIN_RESP";
394 wait(clk_i.posedge_event());
395 if(protocol_cb[axi::fsm::EndRespE])
396 protocol_cb[axi::fsm::EndRespE](trans, true);
397}
398} // namespace pe
399} // namespace axi
cci::cci_param< unsigned > rla
Read last data handshake to acknowledge.
cci::cci_param< unsigned > wbv
Write data handshake to next beat valid.
void b_snoop(payload_type &trans, sc_core::sc_time &t) override
snoop access to a snooped master
cci::cci_param< unsigned > br
Write response valid to ready.
void snoop_resp(payload_type &trans, bool sync=false) override
triggers a non-blocking snoop response if the snoop callback does not do so.
cci::cci_param< unsigned > ba
Write response handshake to acknowledge.
cci::cci_param< unsigned > rbr
Read data valid to same beat ready.
cci::cci_param< unsigned > artv
Read address valid to next read address valid.
void transport(payload_type &trans, bool blocking) override
The forward transport function. It behaves blocking and is re-entrant.
cci::cci_param< unsigned > awtv
Write address valid to next write address valid.
cci::cci_param< bool > enable_id_serializing
Quirks enable.
The ordered_semaphore primitive channel class.
T * get() const noexcept
Return the stored pointer.
protocol engine implementations
TLM2.0 components modeling AHB.
tlm::tlm_fw_transport_if< TYPES > axi_fw_transport_if
alias declaration for the forward interface
Definition axi_tlm.h:954
a lock for the semaphore