scc 2025.09
SystemC components library
simple_initiator.cpp
1/*
2 * Copyright 2020 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.axi_util.cpp
15 */
16
17#include "simple_initiator.h"
18#include <axi/fsm/protocol_fsm.h>
19#include <axi/fsm/types.h>
20#include <scc/report.h>
21#include <systemc>
22#include <tlm/scc/tlm_id.h>
23
24using namespace sc_core;
25using namespace tlm;
26using namespace axi;
27using namespace axi::fsm;
28using namespace axi::pe;
29
30/******************************************************************************
31 * initiator
32 ******************************************************************************/
33#if SYSTEMC_VERSION < 20250221
34SC_HAS_PROCESS(simple_initiator_b);
35#endif
36simple_initiator_b::simple_initiator_b(const sc_core::sc_module_name& nm,
37 sc_core::sc_port_b<axi::axi_fw_transport_if<axi_protocol_types>>& port, size_t transfer_width,
38 bool ack_phase)
39: sc_module(nm)
40, base(transfer_width, ack_phase)
41, socket_fw(port) {
42 add_attribute(wr_data_beat_delay);
43 add_attribute(rd_data_accept_delay);
44 add_attribute(wr_resp_accept_delay);
45 add_attribute(ack_resp_delay);
46 fw_i.bind(*this);
47 SC_METHOD(fsm_clk_method);
48 dont_initialize();
49 sensitive << clk_i.pos();
50 SC_METHOD(process_snoop_resp);
51 sensitive << clk_i.pos();
52 snp_resp_queue.set_avail_cb([this]() {
53 if(snp_resp_queue_hndl.valid())
54 snp_resp_queue_hndl.enable();
55 });
56 snp_resp_queue.set_empty_cb([this]() {
57 if(snp_resp_queue_hndl.valid())
58 snp_resp_queue_hndl.disable();
59 });
60 SC_METHOD(cbpeq_cb);
61 dont_initialize();
62 sensitive << cbpeq.event();
63}
64
65void simple_initiator_b::end_of_elaboration() { clk_if = dynamic_cast<sc_core::sc_clock*>(clk_i.get_interface()); }
66
67// bool simple_initiator_b::operation(bool write, uint64_t addr, unsigned len, const uint8_t* data, bool blocking) {
68void simple_initiator_b::transport(payload_type& trans, bool blocking) {
69 // auto ext = trans.get_extension<axi::axi4_extension>();
70 // sc_assert(ext!=nullptr);
71 SCCTRACE(SCMOD) << "got transport req for trans " << trans;
72 if(blocking) {
73 sc_time t;
74 socket_fw->b_transport(trans, t);
75 } else {
76 fsm_handle* fsm = find_or_create(&trans);
77 if(trans.is_read()) {
78 rd.wait();
79 wait(clk_i.posedge_event());
80 } else {
81 wr.wait();
82 wait(clk_i.posedge_event());
83 }
84 react(RequestPhaseBeg, fsm);
85 SCCTRACE(SCMOD) << "started non-blocking protocol";
86 sc_core::wait(fsm->finish);
87 SCCTRACE(SCMOD) << "finished non-blocking protocol";
88 }
89}
90
92
94 fsm_hndl->fsm->cb[RequestPhaseBeg] = [this, fsm_hndl]() -> void {
95 fsm_hndl->beat_count = 0;
96 auto& f = protocol_cb[RequestPhaseBeg];
97 if(f)
98 f(*fsm_hndl->trans, fsm_hndl->is_snoop);
99 };
100 fsm_hndl->fsm->cb[BegPartReqE] = [this, fsm_hndl]() -> void {
101 sc_time t;
102 tlm::tlm_phase phase = axi::BEGIN_PARTIAL_REQ;
103 auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
104 if(ret == tlm::TLM_UPDATED) {
105 schedule(EndPartReqE, fsm_hndl->trans, t, true);
106 }
107 if((bool)protocol_cb[BegPartReqE])
108 cbpeq.notify(std::make_tuple(BegPartReqE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
109 };
110 fsm_hndl->fsm->cb[EndPartReqE] = [this, fsm_hndl]() -> void {
111 fsm_hndl->beat_count++;
112 if(fsm_hndl->beat_count < (get_burst_length(*fsm_hndl->trans) - 1))
113 if(::scc::get_value(wr_data_beat_delay) > 0)
114 schedule(BegPartReqE, fsm_hndl->trans, ::scc::get_value(wr_data_beat_delay) - 1);
115 else
116 schedule(BegPartReqE, fsm_hndl->trans, SC_ZERO_TIME);
117 else if(::scc::get_value(wr_data_beat_delay) > 0)
118 schedule(BegReqE, fsm_hndl->trans, ::scc::get_value(wr_data_beat_delay) - 1);
119 else
120 schedule(BegReqE, fsm_hndl->trans, 0);
121 if((bool)protocol_cb[EndPartReqE])
122 cbpeq.notify(std::make_tuple(EndPartReqE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
123 };
124 fsm_hndl->fsm->cb[BegReqE] = [this, fsm_hndl]() -> void {
125 if(fsm_hndl->is_snoop) {
126 schedule(EndReqE, fsm_hndl->trans, SC_ZERO_TIME);
127 } else {
128 sc_time t;
129 tlm::tlm_phase phase = tlm::BEGIN_REQ;
130 auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
131 if(ret == tlm::TLM_UPDATED) {
132 schedule(EndReqE, fsm_hndl->trans, t, true);
133 }
134 }
135 if((bool)protocol_cb[BegReqE])
136 cbpeq.notify(std::make_tuple(BegReqE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
137 };
138 fsm_hndl->fsm->cb[EndReqE] = [this, fsm_hndl]() -> void {
139 if(fsm_hndl->is_snoop) {
140 tlm::tlm_phase phase = tlm::END_REQ;
141 sc_time t(clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME);
142 auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
143 auto ext = fsm_hndl->trans->get_extension<ace_extension>();
144 sc_assert(ext && "No ACE extension found for snoop access");
145 fsm_hndl->beat_count = 0;
146 fsm_hndl->trans->set_response_status(tlm::TLM_OK_RESPONSE);
147 auto latency = snoop_latency;
148 if(bw_o.get_interface()) {
149 // since the bw_o interface is blocking we need to use threads to check for the latency in the bw path
150 if(thread_avail == 0) {
151 sc_core::sc_spawn_options opts;
152 opts.set_stack_size(0x10000);
153 sc_core::sc_spawn(
154 [this]() {
155 thread_active++;
156 while(true) {
157 thread_avail++;
158 auto req = dispatch_queue.read();
159 sc_assert(thread_avail > 0);
160 thread_avail--;
161 auto latency = bw_o->transport(*req);
162 if(latency < std::numeric_limits<unsigned>::max()) {
163 auto ext = req->get_extension<ace_extension>();
164 auto length = ext->get_length() + 1;
165 auto evt = ext->is_snoop_data_transfer() && length > 1 ? BegPartRespE : BegRespE;
166 snp_resp_queue.push_back(std::make_tuple(evt, req.get(), latency));
167 }
168 }
169 },
170 nullptr, &opts);
171 }
172 dispatch_queue.write(fsm_hndl->trans);
173 latency = std::numeric_limits<unsigned>::max();
174 } else if(snoop_cb) {
175 latency = snoop_cb(*fsm_hndl->trans);
176 } else {
177 ext->set_snoop_data_transfer(false);
178 ext->set_snoop_error(false);
179 ext->set_pass_dirty(false);
180 ext->set_shared(false);
181 ext->set_snoop_was_unique(false);
182 }
183 if(latency < std::numeric_limits<unsigned>::max()) {
184 auto length = axi::get_burst_length(*fsm_hndl->trans);
185 auto evt = ext->is_snoop_data_transfer() && length > 1 ? BegPartRespE : BegRespE;
186 snp_resp_queue.push_back(std::make_tuple(evt, fsm_hndl->trans.get(), latency));
187 }
188 } else {
189 // auto ext = fsm_hndl->trans->get_extension<axi::axi4_extension>();
190 if(fsm_hndl->trans->is_write())
191 wr.post();
192 else
193 rd.post();
194 }
195 if((bool)protocol_cb[EndReqE])
196 cbpeq.notify(std::make_tuple(EndReqE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
197 };
198 fsm_hndl->fsm->cb[BegPartRespE] = [this, fsm_hndl]() -> void {
199 if(fsm_hndl->is_snoop) {
200 tlm::tlm_phase phase = axi::BEGIN_PARTIAL_RESP;
201 sc_time t;
202 auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
203 } else {
204 if(::scc::get_value(rd_data_accept_delay))
205 schedule(EndPartRespE, fsm_hndl->trans, ::scc::get_value(rd_data_accept_delay));
206 else
207 schedule(EndPartRespE, fsm_hndl->trans, SC_ZERO_TIME);
208 }
209 if((bool)protocol_cb[BegPartRespE])
210 cbpeq.notify(std::make_tuple(BegPartRespE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
211 };
212 fsm_hndl->fsm->cb[EndPartRespE] = [this, fsm_hndl]() -> void {
213 if(fsm_hndl->is_snoop) {
214 auto size = axi::get_burst_length(*fsm_hndl->trans);
215 fsm_hndl->beat_count++;
216 schedule(fsm_hndl->beat_count < (size - 1) ? BegPartRespE : BegRespE, fsm_hndl->trans, 0);
217 } else {
218 sc_time t(clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME);
219 tlm::tlm_phase phase = axi::END_PARTIAL_RESP;
220 auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
221 fsm_hndl->beat_count++;
222 }
223 if((bool)protocol_cb[EndPartRespE])
224 cbpeq.notify(std::make_tuple(EndPartRespE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
225 };
226 fsm_hndl->fsm->cb[BegRespE] = [this, fsm_hndl]() -> void {
227 if(fsm_hndl->is_snoop) {
228 tlm::tlm_phase phase = tlm::BEGIN_RESP;
229 sc_time t;
230 auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
231 } else {
232 auto del = fsm_hndl->trans->is_read() ? ::scc::get_value(rd_data_accept_delay) : ::scc::get_value(wr_resp_accept_delay);
233 if(del)
234 schedule(EndRespE, fsm_hndl->trans, del);
235 else
236 schedule(EndRespE, fsm_hndl->trans, SC_ZERO_TIME);
237 }
238 if((bool)protocol_cb[BegRespE])
239 cbpeq.notify(std::make_tuple(BegRespE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
240 };
241 fsm_hndl->fsm->cb[EndRespE] = [this, fsm_hndl]() -> void {
242 if(fsm_hndl->is_snoop) {
243 snp.post();
244 } else {
245 sc_time t(clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME);
246 tlm::tlm_phase phase = tlm::END_RESP;
247 auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
248 if(coherent) {
249 schedule(Ack, fsm_hndl->trans, ::scc::get_value(ack_resp_delay));
250 } else
251 fsm_hndl->finish.notify(sc_core::SC_ZERO_TIME);
252 }
253 if((bool)protocol_cb[EndRespE])
254 cbpeq.notify(std::make_tuple(EndRespE, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
255 };
256 fsm_hndl->fsm->cb[Ack] = [this, fsm_hndl]() -> void {
257 sc_time t;
258 tlm::tlm_phase phase = axi::ACK;
259 auto ret = socket_fw->nb_transport_fw(*fsm_hndl->trans, phase, t);
260 fsm_hndl->finish.notify(sc_core::SC_ZERO_TIME);
261 if((bool)protocol_cb[Ack])
262 cbpeq.notify(std::make_tuple(Ack, fsm_hndl->trans, fsm_hndl->is_snoop), sc_core::SC_ZERO_TIME);
263 };
264}
265
266tlm_sync_enum simple_initiator_b::nb_transport_bw(payload_type& trans, phase_type& phase, sc_time& t) {
267 auto ret = TLM_ACCEPTED;
268 SCCTRACE(SCMOD) << "nb_transport_bw " << phase << " of trans " << trans;
269 if(phase == END_PARTIAL_REQ || phase == END_REQ) { // read/write
270 schedule(phase == END_REQ ? EndReqE : EndPartReqE, &trans, t, false);
271 } else if(phase == BEGIN_PARTIAL_RESP || phase == BEGIN_RESP) { // read/write response
272 schedule(phase == BEGIN_RESP ? BegRespE : BegPartRespE, &trans, t, false);
273 } else if(phase == BEGIN_REQ) { // snoop read
274 auto fsm_hndl = find_or_create(&trans, true);
275 fsm_hndl->is_snoop = true;
276 schedule(BegReqE, &trans, t);
277 } else if(phase == END_PARTIAL_RESP || phase == END_RESP) { // snoop read response
278 schedule(phase == END_RESP ? EndRespE : EndPartRespE, &trans, t);
279 }
280 return ret;
281}
282
283void simple_initiator_b::b_snoop(payload_type& trans, sc_time& t) {
284 if(bw_o.get_interface()) {
285 auto latency = bw_o->transport(trans);
286 if(latency < std::numeric_limits<unsigned>::max())
287 t += latency * clk_period;
288 } else if(snoop_cb) {
289 auto latency = snoop_cb(trans);
290 if(latency < std::numeric_limits<unsigned>::max())
291 t += latency * clk_period;
292 }
293}
294
295void simple_initiator_b::process_snoop_resp() {
296 if(!snp_resp_queue_hndl.valid())
297 snp_resp_queue_hndl = sc_process_handle(sc_get_current_process_handle());
298 if(snp_resp_queue.avail())
299 while(snp_resp_queue.avail()) {
300 auto entry = snp_resp_queue.front();
301 if(std::get<2>(entry) == 0) {
302 if(snp.get_value() == 0)
303 snp_resp_queue.push_back(entry);
304 else {
305 snp.wait();
306 auto gp = std::get<1>(entry);
307 SCCTRACE(instance_name) << "processing event " << evt2str(std::get<0>(entry)) << " of trans " << *gp;
308 react(std::get<0>(entry), std::get<1>(entry));
309 }
310 } else {
311 std::get<2>(entry) -= 1;
312 snp_resp_queue.push_back(entry);
313 }
314 snp_resp_queue.pop_front();
315 }
316 else
317 // fall asleep if there is nothing to process
318 snp_resp_queue_hndl.disable();
319}
320
321void simple_initiator_b::snoop_resp(payload_type& trans, bool sync) {
322 axi::fsm::fsm_handle* fsm_hndl = active_fsm[&trans];
323 sc_assert(fsm_hndl != nullptr);
324 auto ext = fsm_hndl->trans->get_extension<ace_extension>();
325 auto size = ext->get_length();
326 protocol_time_point_e e = ext->is_snoop_data_transfer() && size > 0 ? BegPartRespE : BegRespE;
327 if(snp.get_value() == 0) {
328 snp_resp_queue.push_back(std::make_tuple(e, &trans, 0));
329 } else {
330 snp.wait();
331 if(sync)
332 schedule(e, fsm_hndl->trans, 0);
333 else
334 react(e, fsm_hndl->trans);
335 }
336}
337
338void simple_initiator_b::cbpeq_cb() {
339 while(cbpeq.has_next()) {
340 auto e = cbpeq.get();
341 protocol_cb[std::get<0>(e)](*std::get<1>(e), std::get<2>(e));
342 }
343}
void setup_callbacks(axi::fsm::fsm_handle *) override
axi::fsm::fsm_handle * create_fsm_handle() override
void snoop_resp(payload_type &trans, bool sync=false) override
triggers a non-blocking snoop response if the snoop callback does not do so.
simple_initiator_b(const sc_core::sc_module_name &nm, sc_core::sc_port_b< axi::axi_fw_transport_if< axi_protocol_types > > &port, size_t transfer_width, bool coherent=false)
sc_core::sc_attribute< unsigned > rd_data_accept_delay
the latency between between BEGIN(_PARTIAL)_RESP and END(_PARTIAL)_RESP (RVALID to RREADY)
sc_core::sc_attribute< unsigned > wr_resp_accept_delay
the latency between between BEGIN_RESP and END_RESP (BVALID to BREADY)
sc_core::sc_attribute< unsigned > ack_resp_delay
the latency between between BEGIN_RESP and END_RESP (BVALID to BREADY)
unsigned snoop_latency
the default snoop latency between request and response phase. Will be overwritten by the return of th...
sc_core::sc_attribute< unsigned > wr_data_beat_delay
the latency between between END(_PARTIAL)_REQ and BEGIN(_PARTIAL)_REQ (AWREADY to AWVALID and WREADY ...
void transport(payload_type &trans, bool blocking) override
The forward transport function. It behaves blocking and is re-entrant.
T * get() const noexcept
Return the stored pointer.
protocol engine implementations
TLM2.0 components modeling AHB.
unsigned get_burst_length(const request &r)
Definition axi_tlm.h:1167
tlm::tlm_fw_transport_if< TYPES > axi_fw_transport_if
alias declaration for the forward interface
Definition axi_tlm.h:954
SystemC TLM.
Definition dmi_mgr.h:19
void react(axi::fsm::protocol_time_point_e event, tlm::scc::tlm_gp_shared_ptr &trans)
triggers the FSM with event and given transaction
Definition base.h:134
base(size_t transfer_width, bool coherent=false, axi::fsm::protocol_time_point_e wr_start=axi::fsm::RequestPhaseBeg)
the constructor
Definition base.cpp:43
void schedule(axi::fsm::protocol_time_point_e e, tlm::scc::tlm_gp_shared_ptr &gp, unsigned cycles)
processes the fsm_sched_queue and propagates events to fsm_clk_queue. Should be registered as falling...
Definition base.h:107
axi::fsm::fsm_handle * find_or_create(payload_type *gp=nullptr, bool ace=false)
retrieve the FSM handle based on the transaction passed. If non exist one will be created
Definition base.cpp:65
tlm::scc::tlm_gp_shared_ptr trans
pointer to the associated AXITLM payload
Definition types.h:62
sc_core::sc_event finish
event indicating the end of the transaction
Definition types.h:68
size_t beat_count
beat count of this transaction
Definition types.h:64
AxiProtocolFsm *const fsm
pointer to the FSM
Definition types.h:60
bool is_snoop
indicator if this is a snoop access
Definition types.h:66
uint8_t get_length() const
get the AxLEN value of the transaction, the value denotes the burst length - 1
Definition axi_tlm.h:1454