quic/qbox
Loading...
Searching...
No Matches
router.h
1/*
2 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
3 * Author: GreenSocs 2022
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8#ifndef _GREENSOCS_BASE_COMPONENTS_ROUTER_H
9#define _GREENSOCS_BASE_COMPONENTS_ROUTER_H
10
11#include <cinttypes>
12#include <vector>
13
14#define THREAD_SAFE true
15#if THREAD_SAFE == true
16#include <mutex>
17#endif
18
19#include <iomanip>
20
21#include <cci_configuration>
22#include <systemc>
23#include <tlm>
24#include <scp/report.h>
25#include <scp/helpers.h>
26
27#include <list>
28
29#include <tlm_utils/multi_passthrough_initiator_socket.h>
30#include <tlm_utils/multi_passthrough_target_socket.h>
31
32#include <tlm-extensions/pathid_extension.h>
33#include <tlm-extensions/underlying-dmi.h>
34#include <cciutils.h>
35#include <router_if.h>
36#include <module_factory_registery.h>
37#include <tlm_sockets_buswidth.h>
38
39namespace gs {
40
41template <unsigned int BUSWIDTH = DEFAULT_TLM_BUSWIDTH>
42class router : public sc_core::sc_module, public gs::router_if<BUSWIDTH>
43{
44 SCP_LOGGER_VECTOR(D);
45 SCP_LOGGER(());
46 SCP_LOGGER((DMI), "dmi");
47
48 using TargetSocket = tlm::tlm_base_target_socket_b<BUSWIDTH, tlm::tlm_fw_transport_if<>,
49 tlm::tlm_bw_transport_if<>>;
50 using InitiatorSocket = tlm::tlm_base_initiator_socket_b<BUSWIDTH, tlm::tlm_fw_transport_if<>,
51 tlm::tlm_bw_transport_if<>>;
55 using gs::router_if<BUSWIDTH>::bound_targets;
56
57private:
58 std::mutex m_dmi_mutex;
59 struct dmi_info {
60 std::set<int> initiators;
61 tlm::tlm_dmi dmi;
62 dmi_info(tlm::tlm_dmi& _dmi) { dmi = _dmi; }
63 };
64 std::map<uint64_t, dmi_info> m_dmi_info_map;
65 dmi_info* in_dmi_cache(tlm::tlm_dmi& dmi)
66 {
67 auto it = m_dmi_info_map.find(dmi.get_start_address());
68 if (it != m_dmi_info_map.end()) {
69 if (it->second.dmi.get_end_address() != dmi.get_end_address()) {
70 SCP_FATAL((DMI)) << "Can't handle that";
71 }
72 return &(it->second);
73 }
74 auto insit = m_dmi_info_map.insert({ dmi.get_start_address(), dmi_info(dmi) });
75 return &(insit.first->second);
76 }
77 void record_dmi(int id, tlm::tlm_dmi& dmi)
78 {
79 auto it = m_dmi_info_map.find(dmi.get_start_address());
80 if (it != m_dmi_info_map.end()) {
81 if (it->second.dmi.get_end_address() != dmi.get_end_address()) {
82 SCP_WARN((DMI)) << "A new DMI overlaps with an old one, invalidating the old one";
83 invalidate_direct_mem_ptr_ts(0, dmi.get_start_address(),
84 dmi.get_end_address()); // id will be ignored
85 }
86 }
87
88 dmi_info* dinfo = in_dmi_cache(dmi);
89 dinfo->initiators.insert(id);
90 }
91
92 void register_boundto(std::string s)
93 {
95 target_info ti = { 0 };
96 ti.name = s;
97 ti.index = bound_targets.size();
99 SCP_DEBUG((D[ti.index])) << "Connecting : " << ti.name;
100 ti.chained = false;
101 auto tmp = name();
102 int i;
103 for (i = 0; i < s.length(); i++)
104 if (s[i] != tmp[i]) break;
105 ti.shortname = s.substr(i);
106
107 bound_targets.push_back(ti);
108 }
109 std::map<uint64_t, std::string> name_map;
110 std::string txn_tostring(target_info* ti, tlm::tlm_generic_payload& trans)
111 {
112 std::stringstream info;
113 const char* cmd = "UNKOWN";
114 switch (trans.get_command()) {
115 case tlm::TLM_IGNORE_COMMAND:
116 info << "IGNORE ";
117 break;
118 case tlm::TLM_WRITE_COMMAND:
119 info << "WRITE ";
120 break;
121 case tlm::TLM_READ_COMMAND:
122 info << "READ ";
123 break;
124 }
125
126 info << " address:"
127 << "0x" << std::hex << trans.get_address();
128 info << " len:" << trans.get_data_length();
129 unsigned char* ptr = trans.get_data_ptr();
130 if ((trans.get_command() == tlm::TLM_READ_COMMAND && trans.get_response_status() == tlm::TLM_OK_RESPONSE) ||
131 (trans.get_command() == tlm::TLM_WRITE_COMMAND &&
132 trans.get_response_status() == tlm::TLM_INCOMPLETE_RESPONSE)) {
133 info << " data:0x";
134 for (int i = trans.get_data_length(); i; i--) {
135 info << std::setw(2) << std::setfill('0') << std::hex << (unsigned int)(ptr[i - 1]);
136 }
137 }
138 info << " " << trans.get_response_string() << " ";
139 for (int i = 0; i < tlm::max_num_extensions(); i++) {
140 if (trans.get_extension(i)) {
141 info << " extn:" << i;
142 }
143 }
144 return info.str();
145 }
146
147public:
148 // make the sockets public for binding
149 initiator_socket_type initiator_socket;
150 tlm_utils::multi_passthrough_target_socket<router<BUSWIDTH>, BUSWIDTH> target_socket;
151
152private:
153 std::vector<target_info> alias_targets;
154 std::vector<target_info*> targets;
155 std::vector<target_info*> id_targets;
156
157 std::vector<PathIDExtension*> m_pathIDPool; // at most one per thread!
158#if THREAD_SAFE == true
159 std::mutex m_pool_mutex;
160#endif
161 void stamp_txn(int id, tlm::tlm_generic_payload& txn)
162 {
163 PathIDExtension* ext = nullptr;
164 txn.get_extension(ext);
165 if (ext == nullptr) {
166#if THREAD_SAFE == true
167 std::lock_guard<std::mutex> l(m_pool_mutex);
168#endif
169 if (m_pathIDPool.size() == 0) {
170 ext = new PathIDExtension();
171 } else {
172 ext = m_pathIDPool.back();
173 m_pathIDPool.pop_back();
174 }
175 txn.set_extension(ext);
176 }
177 ext->push_back(id);
178 }
179 void unstamp_txn(int id, tlm::tlm_generic_payload& txn)
180 {
182 txn.get_extension(ext);
183 assert(ext);
184 assert(ext->back() == id);
185 ext->pop_back();
186 if (ext->size() == 0) {
187#if THREAD_SAFE == true
188 std::lock_guard<std::mutex> l(m_pool_mutex);
189#endif
190 txn.clear_extension(ext);
191 m_pathIDPool.push_back(ext);
192 }
193 }
194
195 void b_transport(int id, tlm::tlm_generic_payload& trans, sc_core::sc_time& delay)
196 {
197 bool found = false;
198 sc_dt::uint64 addr = trans.get_address();
199 auto ti = decode_address(trans);
200 if (!ti) {
201 SCP_WARN(())("Attempt to access unknown register at offset 0x{:x}", addr);
202 trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
203 return;
204 }
205
206 stamp_txn(id, trans);
207 if (!ti->chained) SCP_TRACE((D[ti->index]), ti->name) << "calling b_transport : " << txn_tostring(ti, trans);
208 if (trans.get_response_status() >= tlm::TLM_INCOMPLETE_RESPONSE) {
209 if (ti->use_offset) trans.set_address(addr - ti->address);
210 initiator_socket[ti->index]->b_transport(trans, delay);
211 if (ti->use_offset) trans.set_address(addr);
212 }
213 if (!ti->chained) SCP_TRACE((D[ti->index]), ti->name) << "b_transport returned : " << txn_tostring(ti, trans);
214 unstamp_txn(id, trans);
215 }
216
217 unsigned int transport_dbg(int id, tlm::tlm_generic_payload& trans)
218 {
219 sc_dt::uint64 addr = trans.get_address();
220 auto ti = decode_address(trans);
221 if (!ti) {
222 trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
223 return 0;
224 }
225
226 if (ti->use_offset) trans.set_address(addr - ti->address);
227 SCP_TRACE((D[ti->index]), ti->name) << "calling dbg_transport : " << scp::scp_txn_tostring(trans);
228 unsigned int ret = initiator_socket[ti->index]->transport_dbg(trans);
229 if (ti->use_offset) trans.set_address(addr);
230 return ret;
231 }
232
233 bool get_direct_mem_ptr(int id, tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data)
234 {
235 std::lock_guard<std::mutex> lock(m_dmi_mutex);
236 sc_dt::uint64 addr = trans.get_address();
237
238 tlm::tlm_dmi dmi_data_hole;
239 auto ti = find_hole(addr, dmi_data_hole);
240
242 trans.get_extension(u_dmi);
243 if (u_dmi) {
244 SCP_DEBUG(())
245 ("DMI info 0x{:x} 0x{:x} {}", dmi_data_hole.get_start_address(), dmi_data_hole.get_end_address(),
246 (ti ? "mapped" : "nomap"));
247 u_dmi->add_dmi(this, dmi_data_hole, (ti ? gs::tlm_dmi_ex::dmi_mapped : gs::tlm_dmi_ex::dmi_nomap));
248 }
249
250 if (!ti) {
251 return false;
252 }
253
254 if (ti->use_offset) trans.set_address(addr - ti->address);
255 SCP_TRACE((D[ti->index]), ti->name) << "calling get_direct_mem_ptr : " << scp::scp_txn_tostring(trans);
256 bool status = initiator_socket[ti->index]->get_direct_mem_ptr(trans, dmi_data);
257 if (ti->use_offset) trans.set_address(addr);
258 if (status) {
259 if (ti->use_offset) {
260 assert(dmi_data.get_start_address() < ti->size);
261 dmi_data.set_start_address(ti->address + dmi_data.get_start_address());
262 dmi_data.set_end_address(ti->address + dmi_data.get_end_address());
263 }
264 /* ensure we dont overspill the 'hole' we have in the address map */
265 if (dmi_data.get_start_address() < dmi_data_hole.get_start_address()) {
266 dmi_data.set_dmi_ptr(dmi_data.get_dmi_ptr() +
267 (dmi_data_hole.get_start_address() - dmi_data.get_start_address()));
268 dmi_data.set_start_address(dmi_data_hole.get_start_address());
269 }
270 if (dmi_data.get_end_address() > dmi_data_hole.get_end_address()) {
271 dmi_data.set_end_address(dmi_data_hole.get_end_address());
272 }
273 record_dmi(id, dmi_data);
274 }
275 SCP_DEBUG(())
276 ("Providing DMI (status {:x}) {:x} - {:x}", status, dmi_data.get_start_address(), dmi_data.get_end_address());
277 return status;
278 }
279
280 void invalidate_direct_mem_ptr(int id, sc_dt::uint64 start, sc_dt::uint64 end)
281 {
282 if (id_targets[id]->use_offset) {
283 start = id_targets[id]->address + start;
284 end = id_targets[id]->address + end;
285 }
286 std::lock_guard<std::mutex> lock(m_dmi_mutex);
287 invalidate_direct_mem_ptr_ts(id, start, end);
288 }
289
290 void invalidate_direct_mem_ptr_ts(int id, sc_dt::uint64 start, sc_dt::uint64 end)
291 {
292 auto it = m_dmi_info_map.upper_bound(start);
293
294 if (it != m_dmi_info_map.begin()) {
295 /*
296 * Start with the preceding region, as it may already cross the
297 * range we must invalidate.
298 */
299 it--;
300 }
301 std::set<int> initiators;
302
303 while (it != m_dmi_info_map.end()) {
304 tlm::tlm_dmi& r = it->second.dmi;
305
306 if (r.get_start_address() > end) {
307 /* We've got out of the invalidation range */
308 break;
309 }
310
311 if (r.get_end_address() < start) {
312 /* We are not in yet */
313 it++;
314 continue;
315 }
316 for (auto t : it->second.initiators) {
317 SCP_TRACE((DMI)) << "Queueing initiator " << t << " for invalidation, its bounds are [0x" << std::hex
318 << it->second.dmi.get_start_address() << " - 0x" << it->second.dmi.get_end_address()
319 << "]";
320 initiators.insert(t);
321 }
322 it = m_dmi_info_map.erase(it);
323 }
324 for (auto t : initiators) {
325 SCP_INFO((DMI)) << "Invalidating initiator " << t << " [0x" << std::hex << start << " - 0x" << end << "]";
326 target_socket[t]->invalidate_direct_mem_ptr(start, end);
327 }
328 }
329
330 target_info* decode_address(tlm::tlm_generic_payload& trans)
331 {
332 lazy_initialize();
333
334 sc_dt::uint64 addr = trans.get_address();
335 sc_dt::uint64 len = trans.get_data_length();
336
337 for (auto ti : targets) {
338 if (addr >= ti->address && (addr - ti->address) < ti->size) {
339 return ti;
340 }
341 }
342 return nullptr;
343 }
344
345 target_info* find_hole(uint64_t addr, tlm::tlm_dmi& dmi)
346 {
347 uint64_t end = std::numeric_limits<uint64_t>::max();
348 uint64_t start = 0;
349 for (auto ti : targets) {
350 if (ti->address < end && ti->address > addr) {
351 end = ti->address - 1;
352 }
353 if ((ti->address + ti->size) > start && (ti->address + ti->size) <= addr) {
354 start = ti->address + ti->size;
355 }
356 if (addr >= ti->address && (addr - ti->address) < ti->size) {
357 if (ti->address > start) start = ti->address;
358 if ((ti->address + ti->size) < end) end = ti->address + ti->size;
359 SCP_DEBUG(())("Device found for address {:x} from {:x} to {:x}", addr, start, end);
360 dmi.set_start_address(start);
361 dmi.set_end_address(end);
362 return ti;
363 }
364 }
365 SCP_DEBUG(())("Hole for address {:x} from {:x} to {:x}", addr, start, end);
366 dmi.set_start_address(start);
367 dmi.set_end_address(end);
368 return nullptr;
369 }
370
371protected:
372 virtual void before_end_of_elaboration()
373 {
374 if (!lazy_init) lazy_initialize();
375 }
376
377private:
378 bool initialized = false;
379 void lazy_initialize()
380 {
381 if (initialized) return;
382 initialized = true;
383
384 for (auto& ti : bound_targets) {
385 std::string name = ti.name;
386 std::string src;
387 if (gs::cci_get<std::string>(m_broker, ti.name + ".0", src)) {
388 // deal with an alias
389 if (m_broker.has_preset_value(ti.name + ".address") &&
390 m_broker.get_preset_cci_value(ti.name + ".address").is_number()) {
391 SCP_WARN((D[ti.index]), ti.name)
392 ("The configuration alias provided ({}) will be ignored as a valid address is also provided.", src);
393 } else {
394 if (src[0] == '&') src = (src.erase(0, 1)) + ".target_socket";
395 if (!m_broker.has_preset_value(src + ".address")) {
396 SCP_FATAL((D[ti.index]), ti.name)
397 ("The configuration alias provided ({}) can not be found.", src);
398 }
399 name = src;
400 }
401 }
402
403 ti.address = gs::cci_get<uint64_t>(m_broker, name + ".address");
404 ti.size = gs::cci_get<uint64_t>(m_broker, name + ".size");
405 ti.use_offset = gs::cci_get_d<bool>(m_broker, name + ".relative_addresses", true);
406 ti.chained = gs::cci_get_d<bool>(m_broker, name + ".chained", false);
407 ti.priority = gs::cci_get_d<uint32_t>(m_broker, name + ".priority", 0);
408
409 SCP_INFO((D[ti.index]), ti.name)
410 << "Address map " << ti.name << " at address "
411 << "0x" << std::hex << ti.address << " size "
412 << "0x" << std::hex << ti.size << (ti.use_offset ? " (with relative address) " : "");
413 if (ti.chained) SCP_DEBUG(())("{} is chained so debug will be suppressed", ti.name);
414
415 for (auto tti : targets) {
416 if (tti->address >= ti.address && (ti.address - tti->address) < tti->size) {
417 SCP_WARN((D[ti.index]), ti.name)("{} overlaps with {}", ti.name, tti->name);
418 }
419 }
420
421 targets.push_back(&ti);
422
423 for (std::string n : gs::sc_cci_children((ti.name + ".aliases").c_str())) {
424 std::string name = ti.name + ".aliases." + n;
425 uint64_t address = gs::cci_get<uint64_t>(m_broker, name + ".address");
426 uint64_t size = gs::cci_get<uint64_t>(m_broker, name + ".size");
427 SCP_INFO((D[ti.index]), ti.name)("Adding alias {} {:#x} (size: {})", name, address, size);
428 target_info ati = ti;
429 ati.address = address;
430 ati.size = size;
431 ati.name = name;
432 alias_targets.push_back(ati);
433 }
434 id_targets.push_back(&ti);
435 }
436 for (auto& ati : alias_targets) {
437 targets.push_back(&ati);
438 }
439 if (!targets.empty())
440 std::stable_sort(targets.begin(), targets.end(), [](const target_info* first, const target_info* second) {
441 return first->priority < second->priority;
442 });
443 }
444
445 cci::cci_broker_handle m_broker;
446
447public:
448 cci::cci_param<bool> lazy_init;
449
450 explicit router(const sc_core::sc_module_name& nm, cci::cci_broker_handle broker = cci::cci_get_broker())
451 : sc_core::sc_module(nm)
452 , initiator_socket("initiator_socket", [&](std::string s) -> void { register_boundto(s); })
453 , target_socket("target_socket")
454 , m_broker(broker)
455 , lazy_init("lazy_init", false, "Initialize the router lazily (eg. during simulation rather than BEOL)")
456 {
457 SCP_DEBUG(()) << "router constructed";
458
459 target_socket.register_b_transport(this, &router::b_transport);
460 target_socket.register_transport_dbg(this, &router::transport_dbg);
461 target_socket.register_get_direct_mem_ptr(this, &router::get_direct_mem_ptr);
462 initiator_socket.register_invalidate_direct_mem_ptr(this, &router::invalidate_direct_mem_ptr);
463 SCP_DEBUG((DMI)) << "router Initializing DMI SCP reporting";
464 }
465
466 router() = delete;
467
468 router(const router&) = delete;
469
470 ~router()
471 {
472 while (m_pathIDPool.size()) {
473 delete (m_pathIDPool.back());
474 m_pathIDPool.pop_back();
475 }
476 }
477
478 void add_target(TargetSocket& t, const uint64_t address, uint64_t size, bool masked = true)
479 {
480 std::string s = gs::router_if<BUSWIDTH>::nameFromSocket(t.get_base_export().name());
481 if (!m_broker.has_preset_value(s + ".address")) {
482 m_broker.set_preset_cci_value(s + ".address", cci::cci_value(address));
483 }
484 if (!m_broker.has_preset_value(s + ".size")) {
485 m_broker.set_preset_cci_value(s + ".size", cci::cci_value(size));
486 }
487 if (!m_broker.has_preset_value(s + ".relative_addresses")) {
488 m_broker.set_preset_cci_value(s + ".relative_addresses", cci::cci_value(masked));
489 }
490 initiator_socket.bind(t);
491 }
492
493 virtual void add_initiator(InitiatorSocket& i)
494 {
495 // hand bind the port/exports as we are using base classes
496 (i.get_base_port())(target_socket.get_base_interface());
497 (target_socket.get_base_port())(i.get_base_interface());
498 }
499};
500} // namespace gs
501
502extern "C" void module_register();
503#endif
Definition target.h:160
Definition pathid_extension.h:26
Definition underlying-dmi.h:34
Definition router_if.h:23
Definition router.h:43
Tool which reads a Lua configuration file and sets parameters.
Definition biflow.cc:10
std::list< std::string > sc_cci_children(sc_core::sc_module_name name)
return a list of 'unconsumed' children from the given module name, can be used inside or outside the ...
Definition cciutils.cc:63
Definition router_if.h:48