scc 2025.09
SystemC components library
chi_rn_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
17#ifndef SC_INCLUDE_DYNAMIC_PROCESSES
18#define SC_INCLUDE_DYNAMIC_PROCESSES
19#endif
20#include <atp/timing_params.h>
21#include <axi/axi_tlm.h>
22#include <cache/cache_info.h>
23#include <chi/pe/chi_rn_initiator.h>
24#include <scc/report.h>
25#include <tlm/scc/tlm_gp_shared.h>
26#include <tlm/scc/tlm_mm.h>
27#include <util/strprintf.h>
28
29using namespace sc_core;
30using namespace chi;
31using namespace chi::pe;
32using sem_lock = scc::ordered_semaphore::lock;
33
34namespace {
35uint8_t log2n(uint8_t siz) { return ((siz > 1) ? 1 + log2n(siz >> 1) : 0); }
36inline uintptr_t to_id(tlm::tlm_generic_payload& t) { return reinterpret_cast<uintptr_t>(&t); }
37inline uintptr_t to_id(tlm::tlm_generic_payload* t) { return reinterpret_cast<uintptr_t>(t); }
38void convert_axi4ace_to_chi(tlm::tlm_generic_payload& gp, char const* name, bool legacy_mapping = false) {
39 if(gp.get_data_length() > 64) {
40 SCCWARN(__FUNCTION__) << "Data length of " << gp.get_data_length() << " is not supported by CHI, shortening payload";
41 gp.set_data_length(64);
42 }
43 auto ace_ext = gp.set_extension<axi::ace_extension>(nullptr);
44 auto axi4_ext = gp.set_extension<axi::axi4_extension>(nullptr);
45
46 // Check that either the AXI4 or ACE extension is set
47 sc_assert(ace_ext != nullptr || axi4_ext != nullptr);
48
49 bool is_ace = (ace_ext != nullptr);
50
51 auto* chi_req_ext = new chi::chi_ctrl_extension;
52
53 //---------- Map the fields in AXI4/ACE extension to CHI request extension
54 // 1. Set SRC ID and TGT ID based on Address of transaction?? XXX: Currently hardcoded
55
56 // Map AXI4/ACE ID to CHI TXN ID
57 chi_req_ext->set_txn_id(is_ace ? ace_ext->get_id() : axi4_ext->get_id());
58 chi_req_ext->set_qos(is_ace ? ace_ext->get_qos() : axi4_ext->get_qos());
59 SCCTRACE(name) << "chi_ctrl_extension set TxnID=0x" << std::hex << chi_req_ext->get_txn_id();
60
61 // XXX: Can gp.get_data_length() be mapped to CHI req 'size' field?
62 sc_assert(((gp.get_data_length() & (gp.get_data_length() - 1)) == 0) &&
63 "CHI data size is not a power of 2: Byte transfer: 0->1, 1->2, 2->4, 3->8, .. 6->64, 7->reserved");
64 uint8_t chi_size = log2n(gp.get_data_length());
65 SCCDEBUG(name) << "convert_axi4ace_to_chi: data length = " << gp.get_data_length()
66 << "; Converted data length to chi_size = " << static_cast<unsigned>(chi_size);
67
68 chi_req_ext->req.set_size(chi_size);
69
70 uint8_t mem_attr = 0; // CHI request field MemAttr
71
72 // Set opcode based on snoop, bar and domain
73 if(!is_ace) {
74 // Non coherent
75 sc_assert(gp.is_read() || gp.is_write()); // Read/Write, no cache maintenance
76 chi_req_ext->req.set_opcode(gp.is_read() ? chi::req_optype_e::ReadNoSnp : chi::req_optype_e::WriteNoSnpFull);
77 } else {
78
79 auto axi_gp_cmd = gp.get_command();
80 auto axi_snp = ace_ext->get_snoop();
81 auto axi_domain = ace_ext->get_domain();
82 auto axi_bar = ace_ext->get_barrier();
83 auto axi_atomic = ace_ext->get_atop();
84 // AN-573. If something is cacheable, then set, if something is not cacheable, then don't set snpattr.
85 auto cacheable = ace_ext->is_modifiable();
86 SCCDEBUG(name) << "AXI/ACE: snoop = " << axi::to_char(axi_snp) << ", barrier = " << axi::to_char(axi_bar)
87 << ", domain = " << axi::to_char(axi_domain);
88 if(axi_bar == axi::bar_e::MEMORY_BARRIER) {
89 sc_assert(axi_snp == axi::snoop_e::BARRIER);
90 SCCERR(name) << "Barrier transaction has no mapping in CHI";
91 }
92 chi::req_optype_e opcode{chi::req_optype_e::ReqLCrdReturn};
93
94 if(axi_atomic) {
95 SCCDEBUG(name) << "AWATOP value: " << std::hex << static_cast<unsigned>(axi_atomic);
96 auto atomic_opcode = (axi_atomic >> 4) & 3;
97 auto atomic_subcode = axi_atomic & 7;
98
99 if(atomic_opcode == 1) {
100 const std::array<chi::req_optype_e, 8> atomic_store_opcodes = {
101 chi::req_optype_e::AtomicStoreAdd, chi::req_optype_e::AtomicStoreClr, chi::req_optype_e::AtomicStoreEor,
102 chi::req_optype_e::AtomicStoreSet, chi::req_optype_e::AtomicStoreSmax, chi::req_optype_e::AtomicStoreSmin,
103 chi::req_optype_e::AtomicStoreUmax, chi::req_optype_e::AtomicStoreUmin};
104 opcode = atomic_store_opcodes[atomic_subcode];
105 } else if(atomic_opcode == 2) {
106 const std::array<chi::req_optype_e, 8> atomic_load_opcodes = {
107 chi::req_optype_e::AtomicLoadAdd, chi::req_optype_e::AtomicLoadClr, chi::req_optype_e::AtomicLoadEor,
108 chi::req_optype_e::AtomicLoadSet, chi::req_optype_e::AtomicLoadSmax, chi::req_optype_e::AtomicLoadSmin,
109 chi::req_optype_e::AtomicLoadUmax, chi::req_optype_e::AtomicLoadUmin};
110 opcode = atomic_load_opcodes[atomic_subcode];
111 } else if(axi_atomic == 0x30)
112 opcode = chi::req_optype_e::AtomicSwap;
113 else if(axi_atomic == 0x31)
114 opcode = chi::req_optype_e::AtomicCompare;
115 else
116 SCCERR(name) << "Can't handle AXI AWATOP value: " << axi_atomic;
117
118 chi_req_ext->req.set_opcode(opcode);
119 chi_req_ext->req.set_snp_attr(axi_snp != axi::snoop_e::READ_NO_SNOOP);
120 chi_req_ext->req.set_snoop_me(axi_snp != axi::snoop_e::READ_NO_SNOOP);
121 } else if(gp.is_read()) {
122 switch(axi_snp) {
123 case axi::snoop_e::READ_NO_SNOOP:
124 sc_assert(axi_domain == axi::domain_e::NON_SHAREABLE || axi_domain == axi::domain_e::SYSTEM);
125 opcode = chi::req_optype_e::ReadNoSnp;
126 break;
127 case axi::snoop_e::READ_ONCE:
128 sc_assert(axi_domain == axi::domain_e::INNER_SHAREABLE || axi_domain == axi::domain_e::OUTER_SHAREABLE);
129 opcode = chi::req_optype_e::ReadOnce;
130 chi_req_ext->req.set_snp_attr(cacheable);
131 break;
132 case axi::snoop_e::READ_SHARED:
133 opcode = chi::req_optype_e::ReadShared;
134 break;
135 case axi::snoop_e::READ_CLEAN:
136 opcode = chi::req_optype_e::ReadClean;
137 break;
138 case axi::snoop_e::READ_NOT_SHARED_DIRTY:
139 opcode = chi::req_optype_e::ReadNotSharedDirty;
140 break;
141 case axi::snoop_e::READ_UNIQUE:
142 opcode = chi::req_optype_e::ReadUnique;
143 break;
144 case axi::snoop_e::CLEAN_SHARED:
145 opcode = chi::req_optype_e::CleanShared;
146 gp.set_data_length(0);
147 break;
148 case axi::snoop_e::CLEAN_INVALID:
149 opcode = chi::req_optype_e::CleanInvalid;
150 gp.set_data_length(0);
151 break;
152 case axi::snoop_e::CLEAN_SHARED_PERSIST:
153 opcode = chi::req_optype_e::CleanSharedPersist;
154 gp.set_data_length(0);
155 break;
156 case axi::snoop_e::CLEAN_UNIQUE:
157 opcode = chi::req_optype_e::CleanUnique;
158 gp.set_data_length(0);
159 break;
160 case axi::snoop_e::MAKE_UNIQUE:
161 opcode = chi::req_optype_e::MakeUnique;
162 gp.set_data_length(0);
163 break;
164 case axi::snoop_e::MAKE_INVALID:
165 opcode = chi::req_optype_e::MakeInvalid;
166 gp.set_data_length(0);
167 break;
168 default:
169 SCCWARN(name) << "unexpected read type";
170 break;
171 }
172 chi_req_ext->req.set_opcode(opcode);
173
174 if(axi_snp != axi::snoop_e::READ_NO_SNOOP) {
175 chi_req_ext->req.set_snp_attr(cacheable);
176 }
177 if(opcode == chi::req_optype_e::StashOnceUnique || opcode == chi::req_optype_e::StashOnceShared) {
178 gp.set_data_length(0);
179 gp.set_command(tlm::TLM_IGNORE_COMMAND);
180 if(ace_ext->is_stash_nid_en()) {
181 chi_req_ext->req.set_stash_n_id(ace_ext->get_stash_nid());
182 chi_req_ext->req.set_stash_n_id_valid(true);
183 }
184 if(ace_ext->is_stash_lpid_en()) {
185 chi_req_ext->req.set_stash_lp_id(ace_ext->get_stash_lpid());
186 chi_req_ext->req.set_stash_lp_id_valid(true);
187 }
188 }
189 } else if(gp.is_write()) {
190 switch(axi_snp) {
191 case axi::snoop_e::WRITE_NO_SNOOP:
192 sc_assert(axi_domain == axi::domain_e::NON_SHAREABLE || axi_domain == axi::domain_e::SYSTEM);
193 opcode = gp.get_data_length() == 64 ? chi::req_optype_e::WriteNoSnpFull : chi::req_optype_e::WriteNoSnpPtl;
194 break;
195 case axi::snoop_e::WRITE_UNIQUE:
196 sc_assert(axi_domain == axi::domain_e::INNER_SHAREABLE || axi_domain == axi::domain_e::OUTER_SHAREABLE);
197 opcode = gp.get_data_length() == 64 ? chi::req_optype_e::WriteUniqueFull : chi::req_optype_e::WriteUniquePtl;
198 chi_req_ext->req.set_snp_attr(cacheable);
199 break;
200 case axi::snoop_e::WRITE_LINE_UNIQUE:
201 opcode = chi::req_optype_e::WriteUniqueFull;
202 break;
203 case axi::snoop_e::WRITE_CLEAN: {
204 bool ptl = false;
205 for(auto i = 0; i < gp.get_byte_enable_length(); ++i) {
206 if(gp.get_byte_enable_ptr()[i] == 0) {
207 ptl = true;
208 break;
209 }
210 }
211 if(ptl)
212 opcode = chi::req_optype_e::WriteCleanPtl;
213 else
214 opcode = chi::req_optype_e::WriteCleanFull;
215 break;
216 }
217 case axi::snoop_e::WRITE_BACK:
218 opcode = gp.get_data_length() == 64 ? chi::req_optype_e::WriteBackFull : chi::req_optype_e::WriteBackPtl;
219 break;
220 case axi::snoop_e::EVICT:
221 opcode = chi::req_optype_e::Evict;
222 break;
223 case axi::snoop_e::WRITE_EVICT:
224 opcode = chi::req_optype_e::WriteEvictFull;
225 break;
226 case axi::snoop_e::WRITE_UNIQUE_PTL_STASH:
227 opcode = chi::req_optype_e::WriteUniquePtlStash;
228 break;
229 case axi::snoop_e::WRITE_UNIQUE_FULL_STASH:
230 opcode = chi::req_optype_e::WriteUniqueFullStash;
231 break;
232 case axi::snoop_e::STASH_ONCE_UNIQUE:
233 opcode = chi::req_optype_e::StashOnceUnique;
234 gp.set_data_length(0);
235 chi_req_ext->req.set_size(6); // full cache line
236 break;
237 case axi::snoop_e::STASH_ONCE_SHARED:
238 opcode = chi::req_optype_e::StashOnceShared;
239 gp.set_data_length(0);
240 chi_req_ext->req.set_size(6); // full cache line
241 break;
242 default:
243 SCCWARN(name) << "unexpected snoop type " << axi::to_char(axi_snp) << " during write";
244 break;
245 }
246 chi_req_ext->req.set_opcode(opcode);
247
248 if(axi_snp != axi::snoop_e::WRITE_NO_SNOOP) {
249 chi_req_ext->req.set_snp_attr(cacheable);
250 }
251 if(opcode == chi::req_optype_e::WriteUniquePtlStash || opcode == chi::req_optype_e::WriteUniqueFullStash ||
252 opcode == chi::req_optype_e::StashOnceUnique || opcode == chi::req_optype_e::StashOnceShared) {
253 if(ace_ext->is_stash_nid_en()) {
254 chi_req_ext->req.set_stash_n_id(ace_ext->get_stash_nid());
255 chi_req_ext->req.set_stash_n_id_valid(true);
256 }
257 if(ace_ext->is_stash_lpid_en()) {
258 chi_req_ext->req.set_stash_lp_id(ace_ext->get_stash_lpid());
259 chi_req_ext->req.set_stash_lp_id_valid(true);
260 }
261 }
262 } else {
263 // Must be Cache maintenance. XXX: To do
264 SCCERR(name) << "Not yet implemented !!! ";
265 }
266
267 if(legacy_mapping) {
285 //
286 // Cache Read Write
287 // 0000 0010 Read/Write
288 // 0001 0011 Read/Write
289 // 0010 0000 Read/Write
290 // 0011 0001 Read/Write
291 // 0110 1101 0101 Read Write
292 // 0111 1101 0101 Read Write
293 // 1010 0101 1101 Read Write
294 // 1011 0101 1101 Read Write
295 // 1110 1101 Read/Write
296 // 1111 1101 Read/Write
297
298 switch(ace_ext->get_cache()) {
299 case 0b0000:
300 mem_attr = 0b0010;
301 break;
302 case 0b0001:
303 mem_attr = 0b0011;
304 break;
305 case 0b0010:
306 mem_attr = 0b0000;
307 break;
308 case 0b0011:
309 mem_attr = 0b0001;
310 break;
311 case 0b0110:
312 case 0b0111:
313 mem_attr = gp.is_read() ? 0b1101 : 0b0101;
314 break;
315 case 0b1010:
316 case 0b1011:
317 mem_attr = gp.is_read() ? 0b0101 : 0b1101;
318 break;
319 case 0b1110:
320 case 0b1111:
321 mem_attr = 0b1101;
322 break;
323 default:
324 SCCERR(name) << "Unexpected AxCACHE type";
325 break;
326 }
327 } else {
328 auto allocate = (ace_ext->is_allocate());
329 auto cachable = ace_ext->is_cacheable();
330 auto ewa = ace_ext->is_bufferable();
331 auto device = ace_ext->get_cache() < 2;
332 mem_attr = (allocate ? 8 : 0) + (cachable ? 4 : 0) + (device ? 2 : 0) + (ewa ? 1 : 0);
333 // ReadNoSnp., ReadNoSnpSep., ReadOnce*., WriteNoSnp., WriteNoSnp, CMO., WriteNoSnpZero., WriteUnique.,
334 // WriteUnique, CMO., WriteUniqueZero., Atomic.
335 if(!device)
336 switch(opcode) {
337 case chi::req_optype_e::ReadNoSnp:
338 case chi::req_optype_e::ReadNoSnpSep:
339 case chi::req_optype_e::ReadOnce:
340 case chi::req_optype_e::ReadOnceCleanInvalid:
341 case chi::req_optype_e::ReadOnceMakeInvalid:
342 case chi::req_optype_e::WriteNoSnpPtl:
343 case chi::req_optype_e::WriteNoSnpFull:
344 case chi::req_optype_e::WriteUniquePtl:
345 case chi::req_optype_e::WriteUniqueFull:
346 case chi::req_optype_e::AtomicStoreAdd:
347 case chi::req_optype_e::AtomicStoreClr:
348 case chi::req_optype_e::AtomicStoreEor:
349 case chi::req_optype_e::AtomicStoreSet:
350 case chi::req_optype_e::AtomicStoreSmax:
351 case chi::req_optype_e::AtomicStoreSmin:
352 case chi::req_optype_e::AtomicStoreUmax:
353 case chi::req_optype_e::AtomicStoreUmin:
354 case chi::req_optype_e::AtomicLoadAdd:
355 case chi::req_optype_e::AtomicLoadClr:
356 case chi::req_optype_e::AtomicLoadEor:
357 case chi::req_optype_e::AtomicLoadSet:
358 case chi::req_optype_e::AtomicLoadSmax:
359 case chi::req_optype_e::AtomicLoadSmin:
360 case chi::req_optype_e::AtomicLoadUmax:
361 case chi::req_optype_e::AtomicLoadUmin:
362 case chi::req_optype_e::AtomicSwap:
363 case chi::req_optype_e::AtomicCompare:
364 chi_req_ext->req.set_order(0b00);
365 break;
366 default:
367 break;
368 }
369 }
370 }
371 chi_req_ext->req.set_mem_attr(mem_attr);
372
373 if(auto msg = chi::is_valid_msg(chi_req_ext))
374 SCCFATAL(__FUNCTION__) << "Conversion created an invalid chi request, pls. check the AXI/ACE settings: " << msg;
375
376 if(gp.has_mm())
377 gp.set_auto_extension(chi_req_ext);
378 else {
379 gp.set_extension(chi_req_ext);
380 }
381 delete ace_ext;
382 ace_ext = nullptr;
383 delete axi4_ext;
384 axi4_ext = nullptr;
385 gp.set_extension(ace_ext);
386 gp.set_extension(axi4_ext);
387}
388
389void setExpCompAck(chi::chi_ctrl_extension* const req_e) {
390 switch(req_e->req.get_opcode()) {
391 // Ref : Sec 2.8.3 pg 97
392 case chi::req_optype_e::ReadNoSnpSep: // Ref: Pg 143
393 // Ref : Table 2.7 pg 55
394 case chi::req_optype_e::Evict:
395 case chi::req_optype_e::StashOnceUnique:
396 case chi::req_optype_e::StashOnceShared:
397 case chi::req_optype_e::CleanShared:
398 case chi::req_optype_e::CleanSharedPersist:
399 case chi::req_optype_e::CleanSharedPersistSep:
400 case chi::req_optype_e::CleanInvalid:
401 case chi::req_optype_e::MakeInvalid:
402 // write case
403 case chi::req_optype_e::WriteNoSnpZero:
404 case chi::req_optype_e::WriteNoSnpFull:
405 case chi::req_optype_e::WriteNoSnpPtl:
406 case chi::req_optype_e::WriteUniqueZero:
407 case chi::req_optype_e::WriteUniquePtl:
408 case chi::req_optype_e::WriteUniqueFull:
409 case chi::req_optype_e::WriteUniqueFullStash:
410 case chi::req_optype_e::WriteBackFull:
411 case chi::req_optype_e::WriteBackPtl:
412 case chi::req_optype_e::WriteCleanFull:
413 case chi::req_optype_e::WriteCleanPtl:
414 // Atomics
415 case chi::req_optype_e::AtomicStoreAdd:
416 case chi::req_optype_e::AtomicStoreClr:
417 case chi::req_optype_e::AtomicStoreEor:
418 case chi::req_optype_e::AtomicStoreSet:
419 case chi::req_optype_e::AtomicStoreSmax:
420 case chi::req_optype_e::AtomicStoreSmin:
421 case chi::req_optype_e::AtomicStoreUmax:
422 case chi::req_optype_e::AtomicStoreUmin:
423 case chi::req_optype_e::AtomicLoadAdd:
424 case chi::req_optype_e::AtomicLoadClr:
425 case chi::req_optype_e::AtomicLoadEor:
426 case chi::req_optype_e::AtomicLoadSet:
427 case chi::req_optype_e::AtomicLoadSmax:
428 case chi::req_optype_e::AtomicLoadSmin:
429 case chi::req_optype_e::AtomicLoadUmax:
430 case chi::req_optype_e::AtomicLoadUmin:
431 case chi::req_optype_e::AtomicCompare:
432 case chi::req_optype_e::AtomicSwap:
433
434 // case chi::req_optype_e::ReadNoSnp: //XXX: Temporary for testing
435 // case chi::req_optype_e::ReadOnce: //XXX: Temporary for testing
436 req_e->req.set_exp_comp_ack(false);
437 break;
438
439 // Ref : pg 55
440 case chi::req_optype_e::ReadNoSnp:
441 case chi::req_optype_e::ReadOnce:
442 case chi::req_optype_e::CleanUnique:
443 case chi::req_optype_e::MakeUnique:
444 req_e->req.set_exp_comp_ack(true);
445 break;
446 default: // XXX: Should default ExpCompAck be true or false?
447 req_e->req.set_exp_comp_ack(true);
448 break;
449 }
450
451 // XXX: For Ordered Read, set ExpCompAck. Check once again, not clear
452 if((req_e->req.get_opcode() == chi::req_optype_e::ReadNoSnp || req_e->req.get_opcode() == chi::req_optype_e::ReadOnce) &&
453 (req_e->req.get_order() == 0b10 || req_e->req.get_order() == 0b11)) {
454 req_e->req.set_exp_comp_ack(true);
455 }
456
457 // Ref pg 101: Ordered write => set ExpCompAck (XXX: Check if its true for all Writes)
458 if((req_e->req.get_opcode() >= chi::req_optype_e::WriteEvictFull &&
459 req_e->req.get_opcode() <= chi::req_optype_e::WriteUniquePtlStash) &&
460 (req_e->req.get_order() == 0b10 || req_e->req.get_order() == 0b11)) {
461 req_e->req.set_exp_comp_ack(true);
462 }
463}
464
465bool make_rsp_from_req(tlm::tlm_generic_payload& gp, chi::rsp_optype_e rsp_opcode) {
466 if(auto* ctrl_e = gp.get_extension<chi::chi_ctrl_extension>()) {
467 if(rsp_opcode == chi::rsp_optype_e::CompAck) {
468 if(is_dataless(ctrl_e) || gp.is_write()) {
469 ctrl_e->resp.set_tgt_id(ctrl_e->req.get_tgt_id());
470 ctrl_e->resp.set_trace_tag(ctrl_e->req.is_trace_tag()); // XXX ??
471 if(ctrl_e->req.get_opcode() == chi::req_optype_e::MakeReadUnique) {
472 ctrl_e->set_txn_id(ctrl_e->resp.get_db_id());
473 }
474 } else {
475 auto dat_e = gp.get_extension<chi::chi_data_extension>();
476 ctrl_e->req.set_tgt_id(dat_e->dat.get_home_n_id());
477 ctrl_e->set_src_id(dat_e->get_src_id());
478 ctrl_e->set_qos(dat_e->get_qos());
479 ctrl_e->set_txn_id(dat_e->dat.get_db_id());
480 ctrl_e->resp.set_tgt_id(dat_e->dat.get_tgt_id());
481 ctrl_e->resp.set_trace_tag(dat_e->dat.is_trace_tag()); // XXX ??
482 }
483 ctrl_e->resp.set_opcode(rsp_opcode);
484 return true;
485 } else
486 ctrl_e->resp.set_opcode(rsp_opcode);
487 } else if(auto* snp_e = gp.get_extension<chi::chi_snp_extension>()) {
488 snp_e->resp.set_opcode(rsp_opcode);
489 if(rsp_opcode == chi::rsp_optype_e::CompAck) {
490 if(auto dat_e = gp.get_extension<chi::chi_data_extension>()) {
491 snp_e->set_src_id(dat_e->get_src_id());
492 snp_e->set_qos(dat_e->get_qos());
493 snp_e->set_txn_id(dat_e->dat.get_db_id());
494 snp_e->resp.set_tgt_id(dat_e->dat.get_tgt_id());
495 snp_e->resp.set_trace_tag(dat_e->dat.is_trace_tag()); // XXX ?
496 }
497 return true;
498 }
499 }
500 return false;
501}
502
503} // anonymous namespace
504
505#if SYSTEMC_VERSION < 20250221
506SC_HAS_PROCESS(chi_rn_initiator_b);
507#endif
508chi::pe::chi_rn_initiator_b::chi_rn_initiator_b(sc_core::sc_module_name nm,
509 sc_core::sc_port_b<chi::chi_fw_transport_if<chi_protocol_types>>& port,
510 size_t transfer_width)
511: sc_module(nm)
512, socket_fw(port)
513, transfer_width_in_bytes(transfer_width / 8) {
514 fw_i.bind(*this);
515
516 SC_METHOD(clk_counter);
517 sensitive << clk_i.pos();
518 SC_THREAD(snoop_dispatch);
519}
520
521chi::pe::chi_rn_initiator_b::~chi_rn_initiator_b() {
522 if(tx_state_by_trans.size()) {
523 for(auto& e : tx_state_by_trans)
524 SCCDEBUG(SCMOD) << "unfinished transaction with ptr: " << e.first << " with access address = 0x" << std::hex
525 << ((tlm::tlm_generic_payload*)e.first)->get_address();
526 SCCWARN(SCMOD) << "is still waiting for unfinished transactions with number = " << tx_state_by_trans.size();
527 }
528 for(auto& e : tx_state_by_trans)
529 delete e.second;
530 for(auto p : tx_state_pool)
531 delete p;
532}
533
534void chi::pe::chi_rn_initiator_b::clk_counter() {
535 if(m_clock_counter > 1 && snp_credit_sent.get() < 15 && snp_counter.get() < snp_req_credit_limit.get_value()) {
536 auto credit2send = std::min<unsigned>(15 - snp_credit_sent.get(), std::min<int>(0, snp_req_credit_limit.get_value() - snp_counter.get()));
537 grant_credit(credit2send);
538 snp_credit_sent += credit2send;
539 }
540 m_clock_counter++;
541}
542
543void chi::pe::chi_rn_initiator_b::b_snoop(payload_type& trans, sc_core::sc_time& t) {
544 if(bw_o.get_interface()) {
545 auto latency = bw_o->transport(trans);
546 if(latency < std::numeric_limits<unsigned>::max())
547 t += latency * (clk_if ? clk_if->period() : clk_period);
548 }
549}
550
551void chi::pe::chi_rn_initiator_b::snoop_resp(payload_type& trans, bool sync) {
552 auto req_ext = trans.get_extension<chi_snp_extension>();
553 sc_assert(req_ext != nullptr);
554 auto it = tx_state_by_trans.find(to_id(trans));
555 sc_assert(it != tx_state_by_trans.end());
556 auto* txs = it->second;
557 handle_snoop_response(trans, txs);
558 tx_state_pool.push_back(it->second);
559 tx_state_pool.back()->peq.clear();
560 tx_state_by_trans.erase(to_id(trans));
561 if(trans.has_mm())
562 trans.release();
563}
564
565tlm::tlm_sync_enum chi::pe::chi_rn_initiator_b::nb_transport_bw(payload_type& trans, phase_type& phase, sc_core::sc_time& t) {
566 if(auto snp_ext = trans.get_extension<chi_snp_extension>()) {
567 if(phase == tlm::BEGIN_REQ) {
568 if(trans.has_mm())
569 trans.acquire();
570 snp_credit_sent--;
571 snp_peq.notify(trans, t);
572 if(snp_counter < snp_req_credit_limit.get_value()) {
573 snp_counter++;
574 snp_credit_sent++;
575 trans.set_auto_extension(new chi_credit_extension(credit_type_e::REQ));
576 }
577 } else {
578 auto it = tx_state_by_trans.find(to_id(trans));
579 sc_assert(it != tx_state_by_trans.end());
580 it->second->peq.notify(std::make_tuple(&trans, phase), t);
581 }
582 } else {
583 if(phase == tlm::BEGIN_REQ) {
584 if(auto credit_ext = trans.get_extension<chi_credit_extension>()) {
585 if(credit_ext->type == credit_type_e::REQ) {
586 SCCTRACEALL(SCMOD) << "Received " << credit_ext->count << " req " << (credit_ext->count == 1 ? "credit" : "credits");
587 for(auto i = 0U; i < credit_ext->count; ++i)
588 req_credits.post();
589 }
590 phase = tlm::END_RESP;
591 trans.set_response_status(tlm::TLM_OK_RESPONSE);
592 if(clk_if)
593 t += clk_if->period() - 1_ps;
594 return tlm::TLM_COMPLETED;
595 } else {
596 SCCFATAL(SCMOD) << "Illegal transaction received from HN";
597 }
598 } else {
599 auto it = tx_state_by_trans.find(to_id(trans));
600 sc_assert(it != tx_state_by_trans.end());
601 it->second->peq.notify(std::make_tuple(&trans, phase), t);
602 }
603 }
604 return tlm::TLM_ACCEPTED;
605}
606
607void chi::pe::chi_rn_initiator_b::invalidate_direct_mem_ptr(sc_dt::uint64 start_range, sc_dt::uint64 end_range) {}
608
609void chi::pe::chi_rn_initiator_b::update_data_extension(chi::chi_data_extension* data_ext, payload_type& trans) {
610 auto req_e = trans.get_extension<chi::chi_ctrl_extension>();
611 sc_assert(req_e != nullptr);
612 switch(req_e->req.get_opcode()) {
613 case chi::req_optype_e::WriteNoSnpPtl:
614 case chi::req_optype_e::WriteNoSnpFull:
615 case chi::req_optype_e::WriteUniquePtl:
616 case chi::req_optype_e::WriteUniqueFull:
617 case chi::req_optype_e::WriteUniquePtlStash:
618 case chi::req_optype_e::WriteUniqueFullStash:
619 // CHE-E
620 case chi::req_optype_e::WriteNoSnpFullCleanSh:
621 case chi::req_optype_e::WriteNoSnpFullCleanInv:
622 case chi::req_optype_e::WriteNoSnpFullCleanShPerSep:
623 case chi::req_optype_e::WriteUniqueFullCleanSh:
624 case chi::req_optype_e::WriteUniqueFullCleanShPerSep:
625 case chi::req_optype_e::WriteBackFullCleanShPerSep:
626 case chi::req_optype_e::WriteNoSnpPtlCleanSh:
627 case chi::req_optype_e::WriteNoSnpPtlCleanInv:
628 case chi::req_optype_e::WriteNoSnpPtlCleanShPerSep:
629 case chi::req_optype_e::WriteUniquePtlCleanSh:
630 case chi::req_optype_e::WriteUniquePtlCleanShPerSep:
631 data_ext->dat.set_opcode(chi::dat_optype_e::NonCopyBackWrData);
632 break;
633
634 case chi::req_optype_e::WriteBackFull:
635 case chi::req_optype_e::WriteBackPtl:
636 case chi::req_optype_e::WriteCleanFull:
637 case chi::req_optype_e::WriteCleanPtl:
638 // CHI-E
639 case chi::req_optype_e::WriteBackFullCleanSh:
640 case chi::req_optype_e::WriteBackFullCleanInv:
641 case chi::req_optype_e::WriteCleanFullCleanSh:
642 case chi::req_optype_e::WriteCleanFullCleanShPerSep:
643 case chi::req_optype_e::WriteEvictFull:
644 data_ext->dat.set_opcode(chi::dat_optype_e::CopyBackWrData);
645 break;
646
647 case chi::req_optype_e::AtomicStoreAdd:
648 case chi::req_optype_e::AtomicStoreClr:
649 case chi::req_optype_e::AtomicStoreEor:
650 case chi::req_optype_e::AtomicStoreSet:
651 case chi::req_optype_e::AtomicStoreSmax:
652 case chi::req_optype_e::AtomicStoreSmin:
653 case chi::req_optype_e::AtomicStoreUmax:
654 case chi::req_optype_e::AtomicStoreUmin:
655 data_ext->dat.set_opcode(chi::dat_optype_e::NonCopyBackWrData);
656 break;
657 case chi::req_optype_e::AtomicLoadAdd:
658 case chi::req_optype_e::AtomicLoadClr:
659 case chi::req_optype_e::AtomicLoadEor:
660 case chi::req_optype_e::AtomicLoadSet:
661 case chi::req_optype_e::AtomicLoadSmax:
662 case chi::req_optype_e::AtomicLoadSmin:
663 case chi::req_optype_e::AtomicLoadUmax:
664 case chi::req_optype_e::AtomicLoadUmin:
665 case chi::req_optype_e::AtomicSwap:
666 case chi::req_optype_e::AtomicCompare:
667 data_ext->dat.set_opcode(chi::dat_optype_e::NonCopyBackWrData);
668 break;
669 default:
670 SCCWARN(SCMOD) << " Unable to match req_opcode with data_opcode in write transaction ";
671 }
672 if(data_ext->dat.get_opcode() == chi::dat_optype_e::NonCopyBackWrData) {
673 data_ext->dat.set_resp(chi::dat_resptype_e::NonCopyBackWrData);
674 } else if(data_ext->dat.get_opcode() == chi::dat_optype_e::NCBWrDataCompAck) {
675 data_ext->dat.set_resp(chi::dat_resptype_e::NCBWrDataCompAck);
676 } else if(data_ext->dat.get_opcode() == chi::dat_optype_e::CopyBackWrData) {
677 auto cache_ext = trans.get_extension<::cache::cache_info>();
678 sc_assert(cache_ext != nullptr);
679 auto cache_state = cache_ext->get_state();
680 if(cache_state == ::cache::state::IX) {
681 data_ext->dat.set_resp(chi::dat_resptype_e::CopyBackWrData_I);
682 } else if(cache_state == ::cache::state::UC) {
683 data_ext->dat.set_resp(chi::dat_resptype_e::CopyBackWrData_UC);
684 } else if(cache_state == ::cache::state::SC) {
685 data_ext->dat.set_resp(chi::dat_resptype_e::CopyBackWrData_SC);
686 } else if(cache_state == ::cache::state::UD) {
687 data_ext->dat.set_resp(chi::dat_resptype_e::CopyBackWrData_UD_PD);
688 } else if(cache_state == ::cache::state::SD) {
689 data_ext->dat.set_resp(chi::dat_resptype_e::CopyBackWrData_SD_PD);
690 } else
691 SCCWARN(SCMOD) << " Unable to match cache state with resptype ";
692 } else {
693 SCCWARN(SCMOD) << "Unable to match resptype with WriteData Responses";
694 }
695
696 auto db_id = req_e->resp.get_db_id();
697 data_ext->set_txn_id(db_id);
698 data_ext->set_src_id(req_e->resp.get_tgt_id());
699 data_ext->dat.set_tgt_id(req_e->get_src_id());
700}
701
702void chi::pe::chi_rn_initiator_b::create_data_ext(payload_type& trans) {
703 auto data_ext = new chi::chi_data_extension;
704 update_data_extension(data_ext, trans);
705 trans.set_auto_extension<chi::chi_data_extension>(data_ext);
706}
707
708void chi::pe::chi_rn_initiator_b::send_packet(tlm::tlm_phase phase, payload_type& trans, chi::pe::chi_rn_initiator_b::tx_state* txs) {
709 if(protocol_cb[WDAT])
710 protocol_cb[WDAT](WDAT, trans);
711 sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
712 tlm::tlm_sync_enum ret = socket_fw->nb_transport_fw(trans, phase, delay);
713 if(ret == tlm::TLM_UPDATED) {
714 if(phase == chi::END_PARTIAL_DATA || phase == chi::END_DATA) {
715 if(delay.value())
716 wait(delay);
717 }
718 } else {
719 auto entry = txs->peq.get();
720 sc_assert(std::get<0>(entry) == &trans && (std::get<1>(entry) == chi::END_PARTIAL_DATA || std::get<1>(entry) == chi::END_DATA));
721 }
722 auto timing_e = trans.get_extension<atp::timing_params>();
723 auto delay_in_cycles = (timing_e && timing_e->wbv) ? timing_e->wbv : 1;
724 while(delay_in_cycles) {
725 delay_in_cycles--;
726 wait(clk_i.posedge_event());
727 }
728}
729
730void chi::pe::chi_rn_initiator_b::send_wdata(payload_type& trans, chi::pe::chi_rn_initiator_b::tx_state* txs) {
731 sc_core::sc_time delay;
732 tlm::tlm_phase phase;
733 auto data_ext = trans.get_extension<chi::chi_data_extension>();
734 if(data_ext == nullptr) {
735 create_data_ext(trans);
736 data_ext = trans.get_extension<chi::chi_data_extension>();
737 }
738
739 auto beat_cnt = calculate_beats(trans);
740 SCCDEBUG(SCMOD) << "Starting transaction on channel WDAT : (opcode, cmd, addr, len) = (" << to_char(data_ext->dat.get_opcode()) << ", "
741 << trans.get_command() << ", " << std::hex << trans.get_address() << ", " << trans.get_data_length() << ")";
742 if(!data_interleaving.get_value()) {
743 auto e = trans.get_extension<atp::timing_params>();
744 if(e) {
745 sem_lock l(prio_wdat_chnl);
746 auto clock_count = sc_core::sc_time_stamp().value() / clk_if->period().value();
747 while(clock_count < e->start_soonest) {
748 wait(clk_i.negedge_event());
749 clock_count = sc_core::sc_time_stamp().value() / clk_if->period().value();
750 }
751 wdat_chnl.wait(1);
752 auto time_offset = sc_core::sc_time_stamp() % clk_if->period();
753 } else
754 wdat_chnl.wait();
755 for(auto i = 0U; i < beat_cnt; ++i) {
756 if(i < beat_cnt - 1)
757 phase = chi::BEGIN_PARTIAL_DATA;
758 else
759 phase = chi::BEGIN_DATA;
760
761 // transfer_width_in_bytes is bus_width in bytes, data_ID reference from table 13_42
762 data_ext->dat.set_data_id(i << (transfer_width_in_bytes * 8 / 128 - 1));
763 SCCTRACE(SCMOD) << "WDAT flit with txnid " << data_ext->cmn.get_txn_id()
764 << " data_id = " << (unsigned int)(data_ext->dat.get_data_id()) << " sent. Beat count: " << i << ", addr: 0x"
765 << std::hex << trans.get_address() << ", last=" << (i == (beat_cnt - 1));
766 send_packet(phase, trans, txs);
767 }
768 wdat_chnl.post();
769 } else { // data packet interleaving allowed
770 for(auto i = 0U; i < beat_cnt; ++i) {
771 {
772 sem_lock lck(wdat_chnl);
773 if(i < beat_cnt - 1)
774 phase = chi::BEGIN_PARTIAL_DATA;
775 else
776 phase = chi::BEGIN_DATA;
777
778 data_ext->dat.set_data_id(i << (transfer_width_in_bytes * 8 / 128 - 1));
779 SCCTRACE(SCMOD) << "WDAT flit with txnid " << data_ext->cmn.get_txn_id()
780 << " data_id = " << (unsigned int)(data_ext->dat.get_data_id()) << " sent. Beat count: " << i
781 << ", addr: 0x" << std::hex << trans.get_address() << ", last=" << (i == (beat_cnt - 1));
782 send_packet(phase, trans, txs);
783 }
784 wait(SC_ZERO_TIME); // yield execution to allow others to lock
785 }
786 }
787}
788
789void chi::pe::chi_rn_initiator_b::send_comp_ack(payload_type& trans, tx_state*& txs) {
790 if(make_rsp_from_req(trans, chi::rsp_optype_e::CompAck)) {
791 sem_lock lck(sresp_chnl);
792 SCCDEBUG(SCMOD) << "Send the CompAck response on SRSP channel, addr: 0x" << std::hex << trans.get_address();
793 if(protocol_cb[SRSP])
794 protocol_cb[SRSP](SRSP, trans);
795 tlm::tlm_phase phase = chi::ACK;
796 auto delay = SC_ZERO_TIME;
797 auto ret = socket_fw->nb_transport_fw(trans, phase, delay);
798 if(ret == tlm::TLM_UPDATED && phase == chi::ACK) {
799 if(delay.value())
800 wait(delay);
801 } else {
802 auto entry = txs->peq.get();
803 sc_assert(std::get<0>(entry) == &trans && std::get<1>(entry) == tlm::END_RESP);
804 }
805 wait(clk_i.posedge_event()); // sync to clock before releasing resource
806 }
807}
808
809bool expectCompCMO(chi::chi_ctrl_extension* ext) {
810 switch(ext->req.get_opcode()) {
811 case req_optype_e::WriteBackFullCleanSh:
812 case req_optype_e::WriteBackFullCleanInv:
813 case req_optype_e::WriteBackFullCleanShPerSep:
814 case req_optype_e::WriteCleanFullCleanSh:
815 case req_optype_e::WriteCleanFullCleanShPerSep:
816 case req_optype_e::WriteNoSnpFullCleanSh:
817 case req_optype_e::WriteNoSnpFullCleanInv:
818 case req_optype_e::WriteNoSnpFullCleanShPerSep:
819 case req_optype_e::WriteUniquePtlCleanSh:
820 case req_optype_e::WriteUniqueFullCleanSh:
821 case req_optype_e::WriteUniquePtlCleanShPerSep:
822 case req_optype_e::WriteUniqueFullCleanShPerSep:
823 return true;
824 default:
825 return false;
826 }
827}
828
829bool expectPersist(chi::chi_ctrl_extension* ext) {
830 switch(ext->req.get_opcode()) {
831 case req_optype_e::WriteBackFullCleanShPerSep:
832 case req_optype_e::WriteCleanFullCleanShPerSep:
833 case req_optype_e::WriteNoSnpFullCleanShPerSep:
834 case req_optype_e::WriteUniquePtlCleanShPerSep:
835 case req_optype_e::WriteUniqueFullCleanShPerSep:
836 case req_optype_e::CleanSharedPersistSep:
837 return true;
838 default:
839 return false;
840 }
841}
842
843enum { WAIT_CTRL = 0x1, WAIT_DATA = 0x2, WAIT_COMPCMO = 4, WAIT_PERSIST = 8 };
844void chi::pe::chi_rn_initiator_b::exec_read_write_protocol(const unsigned int txn_id, payload_type& trans,
845 chi::pe::chi_rn_initiator_b::tx_state*& txs) {
846 // TODO: in write case CAIU does not send BEGIN_RESP;
847 sc_core::sc_time delay;
848 auto ctrl_ext = trans.get_extension<chi::chi_ctrl_extension>();
849 unsigned not_finish = WAIT_CTRL;
850 not_finish |= is_dataless(ctrl_ext) ? 0 : WAIT_DATA;
851 not_finish |= expectCompCMO(ctrl_ext) ? WAIT_COMPCMO : 0;
852 not_finish |= expectPersist(ctrl_ext) ? WAIT_PERSIST : 0;
853 auto exp_beat_cnt = calculate_beats(trans);
854 auto beat_cnt = 0U;
855 while(not_finish) {
856 // waiting for response
857 auto entry = txs->peq.get();
858 sc_assert(std::get<0>(entry) == &trans);
859 auto phase = std::get<1>(entry);
860 if(phase == tlm::BEGIN_RESP) {
861 if(chi::is_dataless(ctrl_ext)) {
862 switch(ctrl_ext->resp.get_opcode()) {
863 case chi::rsp_optype_e::Comp: // Response to dataless Make(Read)Unique request
864 if(ctrl_ext->req.get_opcode() == chi::req_optype_e::MakeReadUnique)
865 not_finish &= ~WAIT_CTRL;
866 else
867 switch(ctrl_ext->resp.get_resp()) {
868 case chi::rsp_resptype_e::Comp_I:
869 case chi::rsp_resptype_e::Comp_UC:
870 case chi::rsp_resptype_e::Comp_SC:
871 not_finish &= ~WAIT_CTRL;
872 break;
873 default:
874 break;
875 }
876 break;
877 case chi::rsp_optype_e::CompDBIDResp: // in case of WriteNoSnpZero, which is dataless
878 case chi::rsp_optype_e::CompPersist:
879 case chi::rsp_optype_e::CompCMO:
880 case chi::rsp_optype_e::CompStashDone:
881 not_finish &= ~WAIT_CTRL;
882 break;
883 case chi::rsp_optype_e::Persist:
884 not_finish &= ~WAIT_PERSIST;
885 break;
886 default:
887 break;
888 }
889 not_finish &= ~WAIT_DATA;
890 send_cresp_response(trans);
891 } else if(trans.is_write()) {
892 switch(ctrl_ext->resp.get_opcode()) {
893 case chi::rsp_optype_e::CompCMO:
894 not_finish &= ~WAIT_COMPCMO;
895 send_cresp_response(trans);
896 break;
897 case chi::rsp_optype_e::Persist:
898 not_finish &= ~WAIT_PERSIST;
899 send_cresp_response(trans);
900 break;
901 case chi::rsp_optype_e::CompDBIDResp:
902 not_finish &= ~WAIT_CTRL;
903 /* no break */
904 case chi::rsp_optype_e::DBIDResp:
905 case chi::rsp_optype_e::DBIDRespOrd:
906 send_cresp_response(trans);
907 send_wdata(trans, txs);
908 not_finish &= ~WAIT_DATA;
909 break;
910 case chi::rsp_optype_e::Comp:
911 not_finish &= ~WAIT_CTRL;
912 send_cresp_response(trans);
913 break;
914 default:
915 SCCFATAL(SCMOD) << "Illegal opcode received: " << to_char(ctrl_ext->resp.get_opcode());
916 }
917 } else if(trans.is_read()) {
918 not_finish &= ~WAIT_CTRL;
919 send_cresp_response(trans);
920 }
921 } else if(trans.is_read() && (phase == chi::BEGIN_PARTIAL_DATA || phase == chi::BEGIN_DATA)) {
922 SCCTRACE(SCMOD) << "RDAT flit received. Beat count: " << beat_cnt << ", addr: 0x" << std::hex << trans.get_address();
923 if(protocol_cb[RDAT])
924 protocol_cb[RDAT](RDAT, trans);
925 phase = phase == chi::BEGIN_PARTIAL_DATA ? (tlm::tlm_phase)chi::END_PARTIAL_DATA : (tlm::tlm_phase)END_DATA;
926 delay = clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME;
927 socket_fw->nb_transport_fw(trans, phase, delay);
928 beat_cnt++;
929 if(phase == chi::END_DATA) {
930 not_finish &= ~(WAIT_CTRL | WAIT_DATA); // clear data bit
931 if(beat_cnt != exp_beat_cnt)
932 SCCERR(SCMOD) << "Wrong beat count, expected " << exp_beat_cnt << ", got " << beat_cnt;
933 }
934 } else {
935 SCCFATAL(SCMOD) << "Illegal protocol state (maybe just not implemented?)";
936 }
937 }
938}
939
940void chi::pe::chi_rn_initiator_b::send_cresp_response(payload_type& trans) {
941 auto resp_ext = trans.get_extension<chi::chi_ctrl_extension>();
942 sc_assert(resp_ext != nullptr);
943 if(is_request_order(resp_ext))
944 req_order.post();
945 auto id = (unsigned)(resp_ext->get_txn_id());
946 SCCDEBUG(SCMOD) << "got cresp: src_id=" << (unsigned)resp_ext->get_src_id() << ", tgt_id=" << (unsigned)resp_ext->resp.get_tgt_id()
947 << ", txnid=0x" << std::hex << id << ", " << to_char(resp_ext->resp.get_opcode())
948 << ", resp=" << to_char(resp_ext->resp.get_resp()) << ", db_id=" << (unsigned)resp_ext->resp.get_db_id() << ", addr=0x"
949 << std::hex << trans.get_address() << ")";
950 if(protocol_cb[CRSP])
951 protocol_cb[CRSP](CRSP, trans);
952 tlm::tlm_phase phase = tlm::END_RESP;
953 sc_core::sc_time delay = clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME;
954 socket_fw->nb_transport_fw(trans, phase, delay);
955 wait(clk_i.posedge_event());
956}
957
958void chi::pe::chi_rn_initiator_b::exec_atomic_protocol(const unsigned int txn_id, payload_type& trans,
959 chi::pe::chi_rn_initiator_b::tx_state*& txs) {
960 sc_core::sc_time delay;
961 // waiting for response
962 auto entry = txs->peq.get();
963 sc_assert(std::get<0>(entry) == &trans);
964 auto phase = std::get<1>(entry);
965 if(phase == tlm::BEGIN_RESP) {
966 send_cresp_response(trans);
967 auto resp_ext = trans.get_extension<chi::chi_ctrl_extension>();
968 if(resp_ext->resp.get_opcode() == chi::rsp_optype_e::DBIDResp) {
969 SCCERR(SCMOD) << "CRESP illegal response opcode: " << to_char(resp_ext->resp.get_opcode());
970 }
971 } else {
972 SCCERR(SCMOD) << "Illegal protocol state (maybe just not implemented?) " << phase;
973 }
974
975 auto not_finish = 0b11U; // bit0: sending data ongoing, bit1: receiving data ongoing
976 auto exp_beat_cnt = calculate_beats(trans);
977 auto input_beat_cnt = 0U;
978 auto output_beat_cnt = 0U;
979
980 sem_lock lck(wdat_chnl);
981 while(not_finish) {
984 if(output_beat_cnt < exp_beat_cnt) {
985 ;
986 if(auto data_ext = trans.get_extension<chi::chi_data_extension>()) {
987 update_data_extension(data_ext, trans);
988 } else {
989 create_data_ext(trans);
990 }
991 output_beat_cnt++;
992 SCCDEBUG(SCMOD) << "Atomic send data (txn_id,opcode,cmd,addr,len) = (" << txn_id << ","
993 << to_char(trans.get_extension<chi::chi_data_extension>()->dat.get_opcode()) << ", " << trans.get_command()
994 << ",0x" << std::hex << trans.get_address() << "," << trans.get_data_length() << "), beat=" << output_beat_cnt
995 << "/" << exp_beat_cnt;
996 if(output_beat_cnt < exp_beat_cnt)
997 phase = chi::BEGIN_PARTIAL_DATA;
998 else
999 phase = chi::BEGIN_DATA;
1000 send_packet(phase, trans, txs);
1001 if(output_beat_cnt == exp_beat_cnt) {
1002 wait(clk_i.posedge_event()); // sync to clock before releasing resource
1003 not_finish &= 0x2; // clear bit0
1004 }
1005 }
1007 if(input_beat_cnt < exp_beat_cnt && txs->peq.has_next()) {
1008
1009 // waiting for response
1010 auto entry = txs->peq.get();
1011 sc_assert(std::get<0>(entry) == &trans);
1012 phase = std::get<1>(entry);
1013
1014 if(phase == chi::BEGIN_PARTIAL_DATA || phase == chi::BEGIN_DATA) {
1015 auto data_ext = trans.get_extension<chi::chi_data_extension>();
1016 sc_assert(data_ext);
1017 input_beat_cnt++;
1018 SCCDEBUG(SCMOD) << "Atomic received data (txn_id,opcode,cmd,addr,len)=(" << txn_id << ","
1019 << to_char(data_ext->dat.get_opcode()) << "," << trans.get_command() << ",0x" << std::hex
1020 << trans.get_address() << "," << trans.get_data_length() << "), beat=" << input_beat_cnt << "/"
1021 << exp_beat_cnt;
1022 if(protocol_cb[RDAT])
1023 protocol_cb[RDAT](RDAT, trans);
1024 phase = phase == chi::BEGIN_PARTIAL_DATA ? (tlm::tlm_phase)chi::END_PARTIAL_DATA : (tlm::tlm_phase)END_DATA;
1025 delay = clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME;
1026 socket_fw->nb_transport_fw(trans, phase, delay);
1027 if(phase == chi::END_DATA) {
1028 not_finish &= 0x1; // clear bit1
1029 if(input_beat_cnt != exp_beat_cnt)
1030 SCCERR(SCMOD) << "Wrong beat count, expected " << exp_beat_cnt << ", got " << input_beat_cnt;
1031 }
1032 } else {
1033 SCCERR(SCMOD) << "Illegal protocol state: " << phase;
1034 }
1035 } else if(output_beat_cnt == exp_beat_cnt)
1036 wait(txs->peq.event());
1037 }
1038}
1039
1040void chi::pe::chi_rn_initiator_b::transport(payload_type& trans, bool blocking) {
1041 SCCTRACE(SCMOD) << "got transport req";
1042 if(blocking) {
1043 sc_time t;
1044 socket_fw->b_transport(trans, t);
1045 } else {
1046 auto req_ext = trans.get_extension<chi_ctrl_extension>();
1047 if(!req_ext) {
1048 convert_axi4ace_to_chi(trans, name(), use_legacy_mapping.get_value());
1049 req_ext = trans.get_extension<chi_ctrl_extension>();
1050 sc_assert(req_ext != nullptr);
1051 }
1052 req_ext->set_src_id(src_id.get_value());
1053 req_ext->req.set_tgt_id(tgt_id.get_value());
1054 req_ext->req.set_max_flit(calculate_beats(trans) - 1);
1055 tx_waiting++;
1056 auto it = tx_state_by_trans.find(to_id(trans));
1057 if(it == tx_state_by_trans.end()) {
1058 if(!tx_state_pool.size())
1059 tx_state_pool.push_back(new tx_state(util::strprintf("peq_%d", ++peq_cnt)));
1060 bool success;
1061 std::tie(it, success) = tx_state_by_trans.insert({to_id(trans), tx_state_pool.back()});
1062 tx_state_pool.pop_back();
1063 }
1064 auto& txs = it->second;
1065 auto const txn_id = req_ext->get_txn_id();
1066 if(chi::is_request_order(req_ext)) {
1067 req_order.wait();
1068 }
1069 if(strict_income_order.get_value())
1070 strict_order_sem.wait();
1071 sem_lock txnlck(active_tx_by_id[txn_id]); // wait until running tx with same id is over
1072 tx_waiting4crd++;
1073 tx_waiting--;
1074 if(strict_income_order.get_value())
1075 strict_order_sem.post();
1076 setExpCompAck(req_ext);
1078 auto timing_e = trans.get_extension<atp::timing_params>();
1079 if(timing_e != nullptr) { // TPU as it has been defined in TPU
1080 auto delay_in_cycles = trans.is_read() ? timing_e->artv : timing_e->awtv;
1081 auto current_count = get_clk_cnt();
1082 if(current_count - m_prev_clk_cnt < delay_in_cycles) {
1083 unsigned delta_cycles = delay_in_cycles - (current_count - m_prev_clk_cnt);
1084 while(delta_cycles) {
1085 delta_cycles--;
1086 wait(clk_i.posedge_event());
1087 }
1088 }
1089 } // no timing info in case of STL
1090 {
1091 sem_lock lck(req_chnl);
1092 // Check if Link-credits are available for sending this transaction and wait if not
1093 req_credits.wait();
1094 tx_outstanding++;
1095 tx_waiting4crd--;
1096 SCCTRACE(SCMOD) << "starting transaction with txn_id=" << txn_id;
1097 m_prev_clk_cnt = get_clk_cnt();
1098 SCCTRACE(SCMOD) << "Send REQ, addr: 0x" << std::hex << trans.get_address() << ", TxnID: 0x" << std::hex << txn_id;
1099 if(protocol_cb[REQ])
1100 protocol_cb[REQ](REQ, trans);
1101 tlm::tlm_phase phase = tlm::BEGIN_REQ;
1102 sc_core::sc_time delay;
1103 tlm::tlm_sync_enum ret = socket_fw->nb_transport_fw(trans, phase, delay);
1104 if(ret == tlm::TLM_UPDATED) {
1105 sc_assert(phase == tlm::END_REQ);
1106 wait(delay);
1107 } else {
1108 auto entry = txs->peq.get();
1109 sc_assert(std::get<0>(entry) == &trans && std::get<1>(entry) == tlm::END_REQ);
1110 }
1111 auto credit_ext = trans.get_extension<chi_credit_extension>();
1112 wait(clk_i.posedge_event()); // sync to clock before releasing resource
1113 if(credit_ext) {
1114 if(credit_ext->type == credit_type_e::REQ) {
1115 SCCTRACEALL(SCMOD) << "Received " << credit_ext->count << " req " << (credit_ext->count == 1 ? "credit" : "credits");
1116 for(auto i = 0U; i < credit_ext->count; ++i)
1117 req_credits.post();
1118 trans.set_auto_extension<chi_credit_extension>(nullptr);
1119 }
1120 }
1121 }
1122
1123 if((req_optype_e::AtomicLoadAdd <= req_ext->req.get_opcode()) && (req_ext->req.get_opcode() <= req_optype_e::AtomicCompare))
1124 exec_atomic_protocol(txn_id, trans, txs);
1125 else {
1126 exec_read_write_protocol(txn_id, trans, txs);
1127 bool is_atomic =
1128 req_ext->req.get_opcode() >= req_optype_e::AtomicStoreAdd && req_ext->req.get_opcode() <= req_optype_e::AtomicCompare;
1129 bool compack_allowed = true;
1130 switch(req_ext->req.get_opcode()) {
1131 case req_optype_e::WriteUniqueFullStash:
1132 case req_optype_e::WriteUniquePtlStash:
1133 case req_optype_e::StashOnceShared:
1134 case req_optype_e::StashOnceUnique:
1135 case req_optype_e::WriteBackPtl:
1136 case req_optype_e::WriteBackFull:
1137 case req_optype_e::WriteCleanFull:
1138 case req_optype_e::WriteCleanPtl:
1139 case req_optype_e::CleanSharedPersistSep:
1140 case req_optype_e::WriteEvictFull:
1141 case req_optype_e::WriteUniqueZero:
1142 case req_optype_e::WriteNoSnpZero:
1143 case req_optype_e::StashOnceSepShared:
1144 case req_optype_e::StashOnceSepUnique:
1145 case req_optype_e::WriteBackFullCleanSh:
1146 case req_optype_e::WriteBackFullCleanInv:
1147 case req_optype_e::WriteBackFullCleanShPerSep:
1148 case req_optype_e::WriteCleanFullCleanSh:
1149 case req_optype_e::WriteCleanFullCleanShPerSep:
1150 compack_allowed = false;
1151 break;
1152 default:
1153 break;
1154 }
1155 if(!is_atomic && compack_allowed && req_ext->req.is_exp_comp_ack())
1156 send_comp_ack(trans, txs);
1157 }
1158
1159 trans.set_response_status(tlm::TLM_OK_RESPONSE);
1160 wait(clk_i.posedge_event()); // sync to clock
1161 tx_state_pool.push_back(it->second);
1162 tx_state_pool.back()->peq.clear();
1163 tx_state_by_trans.erase(it);
1164 SCCTRACE(SCMOD) << "finished non-blocking protocol";
1165 any_tx_finished.notify(SC_ZERO_TIME);
1166 tx_outstanding--;
1167 }
1168}
1169
1170void chi::pe::chi_rn_initiator_b::handle_snoop_response(payload_type& trans, chi::pe::chi_rn_initiator_b::tx_state* txs) {
1171 auto ext = trans.get_extension<chi_data_extension>();
1172 tlm::tlm_phase phase;
1173 sc_time delay;
1174 if(ext) {
1175 ext->set_src_id(src_id.get_value());
1176 send_wdata(trans, txs);
1177 snp_counter--;
1178 return;
1179 }
1180 // dataless response or stash
1181 auto snp_ext = trans.get_extension<chi_snp_extension>();
1182 sc_assert(snp_ext != nullptr);
1183
1184 snp_ext->set_src_id(src_id.get_value());
1185 snp_ext->resp.set_tgt_id(snp_ext->get_src_id());
1186 snp_ext->resp.set_db_id(snp_ext->get_txn_id());
1187
1188 phase = tlm::BEGIN_RESP; // SRSP channel
1189 delay = SC_ZERO_TIME;
1190 auto not_finish = snp_ext->resp.get_data_pull() ? 0b11U : 0b10U; // bit0: data is ongoing, bit1: ctrl resp. is ongoing
1191 {
1192 auto e = trans.get_extension<atp::timing_params>();
1193 if(e) {
1194 sem_lock l(prio_sresp_chnl);
1195 while(get_clk_cnt() < e->start_soonest) {
1196 wait(clk_i.negedge_event());
1197 }
1198 sresp_chnl.wait(1);
1199 wait(clk_i.posedge_event());
1200 } else
1201 sresp_chnl.wait();
1202 if(protocol_cb[SRSP])
1203 protocol_cb[SRSP](SRSP, trans);
1204 auto ret = socket_fw->nb_transport_fw(trans, phase, delay);
1205 if(ret == tlm::TLM_UPDATED) {
1206 sc_assert(phase == tlm::END_RESP); // SRSP channel
1207 wait(delay);
1208 not_finish &= 0x1; // clear bit1
1209 }
1210 wait(clk_i.posedge_event()); // sync to clock before releasing resource
1211 sresp_chnl.post();
1212 }
1213 if(snp_ext->resp.get_data_pull() && trans.get_data_length() < 64) {
1214 delete[] trans.get_data_ptr();
1215 trans.set_data_ptr(new uint8_t[64]);
1216 trans.set_data_length(64);
1217 }
1218 auto exp_beat_cnt = calculate_beats(trans);
1219 auto beat_cnt = 0U;
1220 while(not_finish) {
1221 // waiting for response
1222 auto entry = txs->peq.get();
1223 sc_assert(std::get<0>(entry) == &trans);
1224 auto phase = std::get<1>(entry);
1225 if(phase == tlm::END_RESP) {
1226 not_finish &= 0x1; // clear bit1
1227 } else if(snp_ext->resp.get_data_pull() && (phase == chi::BEGIN_PARTIAL_DATA || phase == chi::BEGIN_DATA)) {
1228 SCCTRACE(SCMOD) << "RDAT packet received with phase " << phase << ". Beat count: " << beat_cnt << ", addr: 0x" << std::hex
1229 << trans.get_address();
1230 not_finish &= 0x1; // clear bit1
1231 if(protocol_cb[WDAT])
1232 protocol_cb[WDAT](WDAT, trans);
1233 phase = phase == chi::BEGIN_PARTIAL_DATA ? (tlm::tlm_phase)chi::END_PARTIAL_DATA : (tlm::tlm_phase)END_DATA;
1234 delay = clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME;
1235 socket_fw->nb_transport_fw(trans, phase, delay);
1236 beat_cnt++;
1237 if(phase == chi::END_DATA) {
1238 not_finish &= 0x2; // clear bit0
1239 if(beat_cnt != exp_beat_cnt)
1240 SCCERR(SCMOD) << "Wrong beat count, expected " << exp_beat_cnt << ", got " << beat_cnt;
1241 if(bw_o.get_interface())
1242 bw_o->transport(trans); // FIXME: explain why this needs to be called- Maybe stash?
1243 }
1244
1245 } else {
1246 SCCFATAL(SCMOD) << "Illegal protocol state (maybe just not implemented?)";
1247 }
1248 }
1249 snp_counter--;
1250 wait(clk_i.posedge_event()); // sync to clock before releasing resource
1251 if(snp_ext->resp.get_data_pull())
1252 send_comp_ack(trans, txs);
1253}
1254
1255// This process handles the SNOOP request received from ICN/HN and dispatches them to the snoop_handler threads
1256void chi::pe::chi_rn_initiator_b::snoop_dispatch() {
1257 sc_core::sc_spawn_options opts;
1258 opts.set_stack_size(0x10000);
1259 payload_type* trans{nullptr};
1260 while(true) {
1261 while(!(trans = snp_peq.get_next_transaction())) {
1262 wait(snp_peq.get_event());
1263 }
1264 if(thread_avail == 0 && thread_active < 32) {
1265 sc_core::sc_spawn(
1266 [this]() {
1267 payload_type* trans{nullptr};
1268 thread_avail++;
1269 thread_active++;
1270 while(true) {
1271 while(!(trans = snp_dispatch_que.get_next_transaction()))
1272 wait(snp_dispatch_que.get_event());
1273 sc_assert(thread_avail > 0);
1274 thread_avail--;
1275 this->snoop_handler(trans);
1276 thread_avail++;
1277 }
1278 },
1279 nullptr, &opts);
1280 }
1281 snp_dispatch_que.notify(*trans);
1282 }
1283}
1284
1285void chi::pe::chi_rn_initiator_b::snoop_handler(payload_type* trans) {
1286 auto req_ext = trans->get_extension<chi_snp_extension>();
1287 sc_assert(req_ext != nullptr);
1288 auto const txn_id = req_ext->get_txn_id();
1289
1290 SCCDEBUG(SCMOD) << "Received SNOOP request: (src_id, txn_id, opcode, command, address) = " << req_ext->get_src_id() << ", " << txn_id
1291 << ", " << to_char(req_ext->req.get_opcode()) << ", " << (trans->is_read() ? "READ" : "WRITE") << ", " << std::hex
1292 << trans->get_address() << ")";
1293
1294 auto it = tx_state_by_trans.find(to_id(trans));
1295 if(it == tx_state_by_trans.end()) {
1296 if(!tx_state_pool.size())
1297 tx_state_pool.push_back(new tx_state(util::strprintf("peq_%d", ++peq_cnt)));
1298 bool success;
1299 std::tie(it, success) = tx_state_by_trans.insert({to_id(trans), tx_state_pool.back()});
1300 tx_state_pool.pop_back();
1301 }
1302 auto* txs = it->second;
1303
1304 if(protocol_cb[SNP])
1305 protocol_cb[SNP](SNP, *trans);
1306 sc_time delay = clk_if ? ::scc::time_to_next_posedge(clk_if) - 1_ps : SC_ZERO_TIME;
1307 tlm::tlm_phase phase = tlm::END_REQ;
1308 socket_fw->nb_transport_fw(*trans, phase, delay);
1309 auto cycles = 0U;
1310 if(bw_o.get_interface())
1311 cycles = bw_o->transport(*trans);
1312 if(cycles < std::numeric_limits<unsigned>::max()) {
1313 // we handle the snoop access ourselfs
1314 // for(size_t i = 0; i < cycles + 1; ++i)
1315 // wait(clk_i.posedge_event());
1316 auto clock_count = sc_core::sc_time_stamp().value() / clk_if->period().value();
1317 auto e = new atp::timing_params(clock_count + cycles - 2);
1318 trans->set_auto_extension(e);
1319
1320 handle_snoop_response(*trans, txs);
1321 tx_state_pool.push_back(it->second);
1322 tx_state_pool.back()->peq.clear();
1323 tx_state_by_trans.erase(to_id(trans));
1324 if(trans->has_mm())
1325 trans->release();
1326 }
1327}
1328
1329void chi::pe::chi_rn_initiator_b::grant_credit(unsigned amount) {
1330 tlm::tlm_phase ph = tlm::BEGIN_REQ;
1331 auto t = sc_core::SC_ZERO_TIME;
1332 tlm::scc::tlm_gp_shared_ptr gp = tlm::scc::tlm_mm<chi_protocol_types>::get().allocate<chi_credit_extension>();
1333 auto ext = gp->template get_extension<chi_credit_extension>();
1334 ext->type = credit_type_e::REQ;
1335 ext->count = amount;
1336 socket_fw->nb_transport_fw(*gp, ph, t);
1337}
void transport(payload_type &trans, bool blocking) override
The forward transport function. It behaves blocking and is re-entrant.
void snoop_resp(payload_type &trans, bool sync=false) override
triggers a non-blocking snoop response if the snoop callback does not do so.
void b_snoop(payload_type &trans, sc_core::sc_time &t) override
snoop access to a snooped master
payload_type * allocate()
get a plain tlm_payload_type without extensions
Definition tlm_mm.h:218
const char * to_char(E t)
@ MEMORY_BARRIER
Normal access, respecting barriers.
Definition axi_tlm.h:91
TLM2.0 components modeling CHI.
Definition chi_tlm.cpp:21
tlm::tlm_fw_transport_if< TYPES > chi_fw_transport_if
alias declaration for the forward interface
Definition chi_tlm.h:904
const char * to_char(E t)
std::string strprintf(const std::string format,...)
allocate and print to a string buffer
Definition strprintf.h:35
unsigned int get_id() const
Definition axi_tlm.h:1299
uint8_t get_qos() const
get the AxQOS (quality of service) value
Definition axi_tlm.h:1500
unsigned int get_txn_id() const
Definition chi_tlm.h:1173
a lock for the semaphore
TYPE get()
blocking get
Definition peq.h:141
sc_core::sc_event & event()
get the available event
Definition peq.h:153
static tlm_mm & get()
accessor function of the singleton
Definition tlm_mm.h:338