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 sc_dt::uint64 addr = trans.get_address();
236
237 tlm::tlm_dmi dmi_data_hole;
238 auto ti = find_hole(addr, dmi_data_hole);
239
241 trans.get_extension(u_dmi);
242 if (u_dmi) {
243 SCP_DEBUG(())
244 ("DMI info 0x{:x} 0x{:x} {}", dmi_data_hole.get_start_address(), dmi_data_hole.get_end_address(),
245 (ti ? "mapped" : "nomap"));
246 u_dmi->add_dmi(this, dmi_data_hole, (ti ? gs::tlm_dmi_ex::dmi_mapped : gs::tlm_dmi_ex::dmi_nomap));
247 }
248
249 if (!ti) {
250 return false;
251 }
252
253 if (!m_dmi_mutex.try_lock()) { // if we're busy invalidating, dont grant DMI's
254 return false;
255 }
256
257 if (ti->use_offset) trans.set_address(addr - ti->address);
258 SCP_TRACE((D[ti->index]), ti->name) << "calling get_direct_mem_ptr : " << scp::scp_txn_tostring(trans);
259 bool status = initiator_socket[ti->index]->get_direct_mem_ptr(trans, dmi_data);
260 if (ti->use_offset) trans.set_address(addr);
261 if (status) {
262 if (ti->use_offset) {
263 assert(dmi_data.get_start_address() < ti->size);
264 dmi_data.set_start_address(ti->address + dmi_data.get_start_address());
265 dmi_data.set_end_address(ti->address + dmi_data.get_end_address());
266 }
267 /* ensure we dont overspill the 'hole' we have in the address map */
268 if (dmi_data.get_start_address() < dmi_data_hole.get_start_address()) {
269 dmi_data.set_dmi_ptr(dmi_data.get_dmi_ptr() +
270 (dmi_data_hole.get_start_address() - dmi_data.get_start_address()));
271 dmi_data.set_start_address(dmi_data_hole.get_start_address());
272 }
273 if (dmi_data.get_end_address() > dmi_data_hole.get_end_address()) {
274 dmi_data.set_end_address(dmi_data_hole.get_end_address());
275 }
276 record_dmi(id, dmi_data);
277 }
278 SCP_DEBUG(())
279 ("Providing DMI (status {:x}) {:x} - {:x}", status, dmi_data.get_start_address(), dmi_data.get_end_address());
280 m_dmi_mutex.unlock();
281 return status;
282 }
283
284 void invalidate_direct_mem_ptr(int id, sc_dt::uint64 start, sc_dt::uint64 end)
285 {
286 if (id_targets[id]->use_offset) {
287 start = id_targets[id]->address + start;
288 end = id_targets[id]->address + end;
289 }
290 std::lock_guard<std::mutex> lock(m_dmi_mutex);
291 invalidate_direct_mem_ptr_ts(id, start, end);
292 }
293
294 void invalidate_direct_mem_ptr_ts(int id, sc_dt::uint64 start, sc_dt::uint64 end)
295 {
296 auto it = m_dmi_info_map.upper_bound(start);
297
298 if (it != m_dmi_info_map.begin()) {
299 /*
300 * Start with the preceding region, as it may already cross the
301 * range we must invalidate.
302 */
303 it--;
304 }
305 std::set<int> initiators;
306
307 while (it != m_dmi_info_map.end()) {
308 tlm::tlm_dmi& r = it->second.dmi;
309
310 if (r.get_start_address() > end) {
311 /* We've got out of the invalidation range */
312 break;
313 }
314
315 if (r.get_end_address() < start) {
316 /* We are not in yet */
317 it++;
318 continue;
319 }
320 for (auto t : it->second.initiators) {
321 SCP_TRACE((DMI)) << "Queueing initiator " << t << " for invalidation, its bounds are [0x" << std::hex
322 << it->second.dmi.get_start_address() << " - 0x" << it->second.dmi.get_end_address()
323 << "]";
324 initiators.insert(t);
325 }
326 it = m_dmi_info_map.erase(it);
327 }
328 for (auto t : initiators) {
329 SCP_INFO((DMI)) << "Invalidating initiator " << t << " [0x" << std::hex << start << " - 0x" << end << "]";
330 target_socket[t]->invalidate_direct_mem_ptr(start, end);
331 }
332 }
333
334 target_info* decode_address(tlm::tlm_generic_payload& trans)
335 {
336 lazy_initialize();
337
338 sc_dt::uint64 addr = trans.get_address();
339 sc_dt::uint64 len = trans.get_data_length();
340
341 for (auto ti : targets) {
342 if (addr >= ti->address && (addr - ti->address) < ti->size) {
343 return ti;
344 }
345 }
346 return nullptr;
347 }
348
349 target_info* find_hole(uint64_t addr, tlm::tlm_dmi& dmi)
350 {
351 uint64_t end = std::numeric_limits<uint64_t>::max();
352 uint64_t start = 0;
353 for (auto ti : targets) {
354 if (ti->address < end && ti->address > addr) {
355 end = ti->address - 1;
356 }
357 if ((ti->address + ti->size) > start && (ti->address + ti->size) <= addr) {
358 start = ti->address + ti->size;
359 }
360 if (addr >= ti->address && (addr - ti->address) < ti->size) {
361 if (ti->address > start) start = ti->address;
362 if ((ti->address + ti->size) < end) end = ti->address + ti->size;
363 SCP_DEBUG(())("Device found for address {:x} from {:x} to {:x}", addr, start, end);
364 dmi.set_start_address(start);
365 dmi.set_end_address(end);
366 return ti;
367 }
368 }
369 SCP_DEBUG(())("Hole for address {:x} from {:x} to {:x}", addr, start, end);
370 dmi.set_start_address(start);
371 dmi.set_end_address(end);
372 return nullptr;
373 }
374
375protected:
376 virtual void before_end_of_elaboration()
377 {
378 if (!lazy_init) lazy_initialize();
379 }
380
381private:
382 bool initialized = false;
383 void lazy_initialize()
384 {
385 if (initialized) return;
386 initialized = true;
387
388 for (auto& ti : bound_targets) {
389 std::string name = ti.name;
390 std::string src;
391 if (gs::cci_get<std::string>(m_broker, ti.name + ".0", src)) {
392 // deal with an alias
393 if (m_broker.has_preset_value(ti.name + ".address") &&
394 m_broker.get_preset_cci_value(ti.name + ".address").is_number()) {
395 SCP_WARN((D[ti.index]), ti.name)
396 ("The configuration alias provided ({}) will be ignored as a valid address is also provided.", src);
397 } else {
398 if (src[0] == '&') src = (src.erase(0, 1)) + ".target_socket";
399 if (!m_broker.has_preset_value(src + ".address")) {
400 SCP_FATAL((D[ti.index]), ti.name)
401 ("The configuration alias provided ({}) can not be found.", src);
402 }
403 name = src;
404 }
405 }
406
407 ti.address = gs::cci_get<uint64_t>(m_broker, name + ".address");
408 ti.size = gs::cci_get<uint64_t>(m_broker, name + ".size");
409 ti.use_offset = gs::cci_get_d<bool>(m_broker, name + ".relative_addresses", true);
410 ti.chained = gs::cci_get_d<bool>(m_broker, name + ".chained", false);
411 ti.priority = gs::cci_get_d<uint32_t>(m_broker, name + ".priority", 0);
412
413 SCP_INFO((D[ti.index]), ti.name)
414 << "Address map " << ti.name << " at address "
415 << "0x" << std::hex << ti.address << " size "
416 << "0x" << std::hex << ti.size << (ti.use_offset ? " (with relative address) " : "");
417 if (ti.chained) SCP_DEBUG(())("{} is chained so debug will be suppressed", ti.name);
418
419 for (auto tti : targets) {
420 if (tti->address >= ti.address && (ti.address - tti->address) < tti->size) {
421 SCP_WARN((D[ti.index]), ti.name)("{} overlaps with {}", ti.name, tti->name);
422 }
423 }
424
425 targets.push_back(&ti);
426
427 for (std::string n : gs::sc_cci_children((ti.name + ".aliases").c_str())) {
428 std::string name = ti.name + ".aliases." + n;
429 uint64_t address = gs::cci_get<uint64_t>(m_broker, name + ".address");
430 uint64_t size = gs::cci_get<uint64_t>(m_broker, name + ".size");
431 SCP_INFO((D[ti.index]), ti.name)("Adding alias {} {:#x} (size: {})", name, address, size);
432 target_info ati = ti;
433 ati.address = address;
434 ati.size = size;
435 ati.name = name;
436 alias_targets.push_back(ati);
437 }
438 id_targets.push_back(&ti);
439 }
440 for (auto& ati : alias_targets) {
441 targets.push_back(&ati);
442 }
443 if (!targets.empty())
444 std::stable_sort(targets.begin(), targets.end(), [](const target_info* first, const target_info* second) {
445 return first->priority < second->priority;
446 });
447 }
448
449 cci::cci_broker_handle m_broker;
450
451public:
452 cci::cci_param<bool> lazy_init;
453
454 explicit router(const sc_core::sc_module_name& nm, cci::cci_broker_handle broker = cci::cci_get_broker())
455 : sc_core::sc_module(nm)
456 , initiator_socket("initiator_socket", [&](std::string s) -> void { register_boundto(s); })
457 , target_socket("target_socket")
458 , m_broker(broker)
459 , lazy_init("lazy_init", false, "Initialize the router lazily (eg. during simulation rather than BEOL)")
460 {
461 SCP_DEBUG(()) << "router constructed";
462
463 target_socket.register_b_transport(this, &router::b_transport);
464 target_socket.register_transport_dbg(this, &router::transport_dbg);
465 target_socket.register_get_direct_mem_ptr(this, &router::get_direct_mem_ptr);
466 initiator_socket.register_invalidate_direct_mem_ptr(this, &router::invalidate_direct_mem_ptr);
467 SCP_DEBUG((DMI)) << "router Initializing DMI SCP reporting";
468 }
469
470 router() = delete;
471
472 router(const router&) = delete;
473
474 ~router()
475 {
476 while (m_pathIDPool.size()) {
477 delete (m_pathIDPool.back());
478 m_pathIDPool.pop_back();
479 }
480 }
481
482 void add_target(TargetSocket& t, const uint64_t address, uint64_t size, bool masked = true)
483 {
484 std::string s = gs::router_if<BUSWIDTH>::nameFromSocket(t.get_base_export().name());
485 if (!m_broker.has_preset_value(s + ".address")) {
486 m_broker.set_preset_cci_value(s + ".address", cci::cci_value(address));
487 }
488 if (!m_broker.has_preset_value(s + ".size")) {
489 m_broker.set_preset_cci_value(s + ".size", cci::cci_value(size));
490 }
491 if (!m_broker.has_preset_value(s + ".relative_addresses")) {
492 m_broker.set_preset_cci_value(s + ".relative_addresses", cci::cci_value(masked));
493 }
494 initiator_socket.bind(t);
495 }
496
497 virtual void add_initiator(InitiatorSocket& i)
498 {
499 // hand bind the port/exports as we are using base classes
500 (i.get_base_port())(target_socket.get_base_interface());
501 (target_socket.get_base_port())(i.get_base_interface());
502 }
503};
504} // namespace gs
505
506extern "C" void module_register();
507#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