SystemC Library API Reference Manual
Reference documentation for the Simics SystemC Library.
 
Loading...
Searching...
No Matches
pcie_mapping_interconnect.h
Go to the documentation of this file.
1/* -*- C++ -*-
2
3 © 2024 Intel Corporation
4
5 This software and the related documents are Intel copyrighted materials, and
6 your use of them is governed by the express license under which they were
7 provided to you ("License"). Unless the License provides otherwise, you may
8 not use, modify, copy, publish, distribute, disclose or transmit this software
9 or the related documents without Intel's prior written permission.
10
11 This software and the related documents are provided as is, with no express or
12 implied warranties, other than those that are expressly stated in the License.
13*/
14
15#ifndef SIMICS_SYSTEMC_COMPOSITE_PCIE_MAPPING_INTERCONNECT_H
16#define SIMICS_SYSTEMC_COMPOSITE_PCIE_MAPPING_INTERCONNECT_H
17
18#include <systemc>
19#include <tlm>
20#include <tlm_utils/multi_socket_bases.h>
21#include <tlm_utils/simple_initiator_socket.h>
22#include <tlm_utils/simple_target_socket.h>
23
24#include <simics/cc-api.h>
31
32#include <sstream>
33#include <map>
34#include <vector>
35#include <stdexcept>
36#include <string>
37#include <set>
38#include <utility> // pair, ignore
39
40namespace { // NOLINT(build/namespaces_headers)
41const char *const PCIE_MAPPING_INTERCONNECT_TAG = \
42 "intel/PcieMappingInterconnect";
43
45const char *pcieTypeName(simics::types::pcie_type_t t) {
46 switch (t) {
48 return "Not Set";
50 return "Memory";
52 return "I/O";
54 return "Config";
56 return "Message";
58 return "Other";
59 default:
60 return "Unknown";
61 }
62}
63
64std::string pcieDeviceIdStr(uint16_t device_id) {
65 std::ostringstream os;
66 os << "device id 0x" << std::hex << device_id
67 << " (" << std::dec << (device_id >> 8)
68 << ":" << ((device_id >> 3) & 0x1f)
69 << "." << (device_id & 7) << ")";
70 return os.str();
71}
72} // namespace
73
74namespace simics {
75namespace systemc {
76namespace composite {
77
80template<typename TSocket>
82 : public iface::ExtensionSender<TSocket> {
83 public:
85
86 // iface::ExtensionSender
90 }
94 }
95
97};
98
105template <unsigned int BUSWIDTH, typename TYPES>
106class PcieMappingInterconnect : public sc_core::sc_module {
107 // Socket types used by the interconnect
108 typedef tlm_utils::simple_target_socket<
109 PcieMappingInterconnect, BUSWIDTH, TYPES> target_socket_t;
110 typedef tlm_utils::simple_initiator_socket<
111 PcieMappingInterconnect, BUSWIDTH, TYPES> initiator_socket_t;
112 typedef struct {
113 tlm::tlm_target_socket<BUSWIDTH, TYPES> *tlm;
114 tlm_utils::multi_target_base<BUSWIDTH, TYPES> *multi;
115 } supported_target_socket_t;
116
117 public:
118#ifndef SYSTEMC_3_0_0
120#endif
122 sc_core::sc_module_name = "PcieMappingInterconnect")
123 : transaction_target_socket("transaction_target_socket"),
124 pcie_device_target_socket("dummy_pcie_device_target_socket"),
125 pcie_map_initiator_socket("pcie_map_initiator_socket"),
126 initiator_sockets_("initiator_socket"),
127 config_target_socket_(),
128 msg_target_socket_(),
129 pcie_map_target_socket_("pcie_map_target_socket") {
130 transaction_target_socket.register_b_transport(
131 this, &PcieMappingInterconnect::transaction_b_transport);
132 transaction_target_socket.register_transport_dbg(
133 this, &PcieMappingInterconnect::transaction_transport_dbg);
134 pcie_map_initiator_socket.register_invalidate_direct_mem_ptr(
135 this, &PcieMappingInterconnect::pcie_map_invalidate_direct_mem_ptr);
136 pcie_map_target_socket_.register_b_transport(
137 this, &PcieMappingInterconnect::pcie_map_b_transport);
138 pcie_map_target_socket_.register_transport_dbg(
139 this, &PcieMappingInterconnect::pcie_map_transport_dbg);
140 pcie_map_target_socket_.register_get_direct_mem_ptr(
141 this, &PcieMappingInterconnect::pcie_map_get_direct_mem_ptr);
142
144 pcie_map_extension_.init(&sender_);
145
146 // f0 is required
147 function_data_[0];
148
149 SC_METHOD(warmReset);
150 sensitive << warm_reset_pin.pos();
151 dont_initialize();
152
153 config_target_socket_.tlm = nullptr;
154 config_target_socket_.multi = nullptr;
155 msg_target_socket_.tlm = nullptr;
156 msg_target_socket_.multi = nullptr;
157 }
158
159 // sc_core::sc_module
161
162 // Called by (outer) composite class from PcieDevice::connected
163 void connected(uint16_t device_id);
164
165 // Called by (outer) composite class from PcieDevice::disconnected
166 void disconnected(uint16_t device_id);
167
168 // Called by (outer) composite class from PcieDevice::hot_reset
169 void hotReset();
170
179 ConfObjectRef o);
180
181 // Create map helpers which are mapped on the cfg space. It contains
182 // PCI information like PCIe type and function id which is used to
183 // route the transaction to the right TLM2 socket
185
186 // Return the current map from address range to socket ID (Memory)
187 std::map<std::pair<size_t, size_t>, size_t> addressIdMemMap() const;
188
189 // Return the current map from address range to socket ID (IO)
190 std::map<std::pair<size_t, size_t>, size_t> addressIdIoMap() const;
191
192 // Sockets bound by external logic (see also private scope):
193 // Simics -> IC, intercepts the incoming PCIe transactions
195 // Dummy socket as PcieDevice is handled by connected/disconnected/hotReset
198 initiator_socket_t pcie_map_initiator_socket;
199 // Simics -> IC, receives warm reset signal
200 sc_core::sc_in<bool> warm_reset_pin;
201 // For MEM/IO, this parameter controls if the base address of the
202 // memory range is subtracted before sending the GP payload to endpoint
204
205 private:
206 // Called when warm_reset_pin is positive
207 void warmReset();
208
209 // Help methods
210 // Adds the BAR mappings of type with map info
211 void addMap(types::map_info_t info, types::pcie_type_t type);
212
213 // Removes the BAR mappings of type for socket ID
214 void delMap(size_t socket_id, types::pcie_type_t type);
215
216 // The supplied device_id passed to addFunction & delFunction should
217 // contain the full function ID and not just the function number.
218 // Adds the function's configuration header in the upstream target
219 void addFunction(uint16_t device_id);
220 // Removes the current mapping (if any) of this function's configuration
221 // header in the upstream target
222 void delFunction(uint16_t device_id);
223
224 // Cast sc_object to a supported target socket
225 void castToTarget(sc_core::sc_object *object,
226 supported_target_socket_t *target);
227
228 // Update the PCIe mapping
229 // @param function_id the config id used to index into function_data_,
230 // -1 means checks all config ids
231 // @param bar_id the bar id used to index into base_address,
232 // -1 means checks all bar ids
233 void updateMappings(int function_id = -1, int bar_id = -1);
234
235 /*
236 * When command register is written to, the mappings need to be re-evaluated
237 * and possibly updated.
238 *
239 * There is no error handling, so if the mapping fails this will only be
240 * logged but the access will still pass (i.e. transaction forwarded to
241 * target device where registers are updated without side-effects)
242 */
243 void updateCommand(uint16_t function_id, uint16_t old_command);
244
245 // Get up-to-date BAR/CMD information whenever we need it. It cannot be
246 // cached in the interconnect module as it might change over time by the
247 // model, reverse execution, checkpoint restore, ...
248 // @param id is the accessing initiator socket ID
249 // @param bar_id is the BAR number currently access, or -1 for config
250 // register access in which case all BARs must be retrieved
251 void retrieveFunctionData(size_t socket_id, int bar_id);
252
253 // Intercept Config space header write access and update the mapping table
254 void interceptCfgHeaderAccess(size_t socket_id,
255 tlm::tlm_generic_payload &trans); // NOLINT
256
257 // Find the id of the initiator_sockets_ list to route the transaction to
258 // based on the PCIe transaction type, function number and offset
259 size_t findSocketId(types::pcie_type_t type, uint16_t function_id,
260 tlm::tlm_generic_payload &trans) const; // NOLINT
261
262 // Return the created cfg map helper by device_id
263 conf_object_t *getCfgMapHelper(uint16_t device_id);
264
265 // Validate and update TLM2 response status
266 // If it passes validation it returns the socket-id as well
267 size_t validateTransaction(tlm::tlm_generic_payload &trans); // NOLINT
268 /*
269 * The single entry for all incoming PCIe transactions from Simics
270 * Depends on the PCIe type (CFG/MEM/IO/MSG), the transaction is
271 * routed to the corresponding initiator socket towards the endpoint device
272 * For CFG space header write, updating the mapping table as well
273 */
274 void transaction_b_transport(tlm::tlm_generic_payload &trans, // NOLINT
275 sc_core::sc_time &t); // NOLINT
276 unsigned int transaction_transport_dbg(
277 tlm::tlm_generic_payload &trans); // NOLINT
278
279 void pcie_map_invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
280 sc_dt::uint64 end_range);
281 void pcie_map_b_transport(
282 tlm::tlm_generic_payload &trans, // NOLINT: SystemC API
283 sc_core::sc_time &t); // NOLINT: SystemC API
284 unsigned int pcie_map_transport_dbg(
285 tlm::tlm_generic_payload &trans); // NOLINT: SystemC API
286 bool pcie_map_get_direct_mem_ptr(
287 tlm::tlm_generic_payload &trans, // NOLINT: SystemC API
288 tlm::tlm_dmi &dmi_data); // NOLINT: SystemC API
289
290 // Sockets created and bound by internal logic (see also public scope):
291 // IC -> Device, forwards the incoming PCIe transactions
292 sc_core::sc_vector<initiator_socket_t> initiator_sockets_;
293 supported_target_socket_t config_target_socket_;
294 supported_target_socket_t msg_target_socket_;
295 // Device -> IC, intercept upstream pcie-map transactions
296 target_socket_t pcie_map_target_socket_;
297
298 struct cfg_header_t {
299 uint16_t command; // command register
300 uint32_t base_address[7]; // 6 BARs + expansion ROM BAR
301 };
302 // Cache each function's cfg header, f0 is required
303 std::map<uint16_t, cfg_header_t> function_data_;
304 std::vector<iface::PcieBaseAddressRegisterQueryInterface::PCIeBar>
305 bar_info_;
306
307 // Extension and sender for talking to the Simics pcie upstream target
308 PcieMappingInterconnectExtensionSender<initiator_socket_t> sender_;
309 iface::PcieMapExtension pcie_map_extension_;
310
311 // Trigger hot reset on the device
312 iface::PcieResetInterface *reset_ {nullptr};
313
314 // A map to store MEM address ranges and corresponding socket IDs
315 std::map<std::pair<size_t, size_t>, size_t> address_id_mem_map_;
316 // A map to store IO address ranges and corresponding socket IDs
317 std::map<std::pair<size_t, size_t>, size_t> address_id_io_map_;
318 // A map from function_number/bar_offset to socket IDs
319 std::map<std::pair<size_t, size_t>, size_t> fn_bar_id_map_;
320
321 ConfObjectRef simulation_obj_ {nullptr};
322 // For PCIe gasket object, the pcie_device and transaction interfaces
323 // are implemented on the gasket_obj_. For other cases, it is the same
324 // object as simulation_obj_
325 ConfObjectRef gasket_obj_ {nullptr};
326
327 // In some case (for example ARI), function number has 8 bits
328 // This boolean variable is set when the function number from getBarInfo()
329 // returns a value greater than 7
330 bool function_number_has_8bits_ {false};
331};
332
333template <unsigned int BUSWIDTH, typename TYPES>
335 // Because of the way SystemC has been designed, we cannot create new
336 // sockets dynamically ouside of the CTOR except for in the
337 // before_end_of_elaboration() method.
338
339 // Create initiator sockets equal to the number of Config, Mem/IO
340 // target sockets and Msg target socket
341 initiator_sockets_.init(function_data_.size() + bar_info_.size() + 1);
342
343 size_t socket_id = 0;
344 // Allocate and initialize function data based on device configuration
345 for (; socket_id < function_data_.size(); ++socket_id) {
346 if (config_target_socket_.tlm) {
347 initiator_sockets_[socket_id].bind(*config_target_socket_.tlm);
348 } else {
349 initiator_sockets_[socket_id].bind(*config_target_socket_.multi);
350 }
351 }
352
353 for (auto &bar : bar_info_) {
354 supported_target_socket_t target = {};
355 castToTarget(bar.target_socket, &target);
356 if (target.tlm) {
357 initiator_sockets_[socket_id].bind(*target.tlm);
358 } else if (target.multi) {
359 initiator_sockets_[socket_id].bind(*target.multi);
360 } else {
361 SC_REPORT_ERROR(
362 PCIE_MAPPING_INTERCONNECT_TAG,
363 "BAR target_socket type is not supported");
364 }
365 fn_bar_id_map_[std::make_pair(bar.function, bar.offset)] = socket_id;
366 ++socket_id;
367 }
368
369 if (msg_target_socket_.tlm) {
370 initiator_sockets_[socket_id].bind(*msg_target_socket_.tlm);
371 } else {
372 initiator_sockets_[socket_id].bind(*msg_target_socket_.multi);
373 }
374}
375
376template <unsigned int BUSWIDTH, typename TYPES>
378 // Use the side effect of it to set device object on pcie map gasket.
379 // The device object is used internally for get the mapping helper port
380 // object
381 pcie_map_extension_.get_device_id(gasket_obj_);
382
383 // The PCIe Modeling Library requires endpoints to add their functions
384 // and map resources like Memory and I/O BARs. However, it is unlikely
385 // that these resources are configured before the connection. Therefore,
386 // we deviate from the PCIe DML Modeling Library by not performing the
387 // mapping at this stage.
388 size_t socket_id = 0;
389 for (const auto &fd : function_data_) {
390 delFunction(device_id | fd.first);
391 addFunction(device_id | fd.first);
392 }
393}
394
395template <unsigned int BUSWIDTH, typename TYPES>
397disconnected(uint16_t device_id) {
398 for (auto &fd : function_data_) {
399 delFunction(device_id | fd.first);
400 // disabled both MEM and IO
401 fd.second.command = 0;
402 updateMappings(fd.first);
403 }
404}
405
406template <unsigned int BUSWIDTH, typename TYPES>
408 if (reset_) {
409 reset_->hotReset();
410 for (auto &fd : function_data_) {
411 // disabled both MEM and IO
412 fd.second.command = 0;
413 updateMappings(fd.first);
414 }
415 }
416}
417
418template <unsigned int BUSWIDTH, typename TYPES>
420 if (reset_) {
421 reset_->warmReset();
422 for (auto &fd : function_data_) {
423 // disabled both MEM and IO
424 fd.second.command = 0;
425 updateMappings(fd.first);
426 }
427 }
428}
429
430template <unsigned int BUSWIDTH, typename TYPES>
431void PcieMappingInterconnect<BUSWIDTH, TYPES>::
432addFunction(uint16_t device_id) {
433 std::ostringstream os;
434 os << "Adding function for device_id: "
435 << device_id;
436 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
437 auto *map_helper = getCfgMapHelper(device_id);
438 if (map_helper == nullptr) {
439 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
440 "Failure to get map helper");
441 return;
442 }
443 pcie_map_extension_.add_function(map_helper, device_id);
444 if (sender_.failed_transaction_.payload()) {
445 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
446 "Failure to add function");
447 return;
448 }
449}
450
451template <unsigned int BUSWIDTH, typename TYPES>
452void PcieMappingInterconnect<BUSWIDTH, TYPES>::
453delFunction(uint16_t device_id) {
454 std::ostringstream os;
455 os << "Deleting function for device_id: "
456 << device_id;
457 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
458 auto *map_helper = getCfgMapHelper(device_id);
459 if (map_helper == nullptr) {
460 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
461 "Failure to get map helper");
462 return;
463 }
464 pcie_map_extension_.del_function(map_helper, device_id);
465 if (sender_.failed_transaction_.payload()) {
466 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
467 "Failure to delete function");
468 return;
469 }
470}
471
472template <unsigned int BUSWIDTH, typename TYPES>
476 iface::PcieResetInterface *reset, ConfObjectRef o) {
477 if (pci == nullptr) {
478 SIM_LOG_ERROR(
480 "PcieDeviceQueryInterface not implemented on the device");
481 return;
482 }
483
484 if (bar == nullptr) {
485 SIM_LOG_ERROR(
487 "PcieBaseAddressRegisterQueryInterface not implemented on"
488 " the device");
489 return;
490 }
491
492 if (reset == nullptr) {
493 SIM_LOG_ERROR(o, Log_Configuration,
494 "PcieResetInterface not implemented on the device");
495 return;
496 }
497
498 // Get the config target_socket from the device
499 castToTarget(pci->getConfigTargetSocket(), &config_target_socket_);
500
501 // Get the message target_socket from the device
502 castToTarget(pci->getMsgTargetSocket(), &msg_target_socket_);
503
504 // Get the BAR info
505 bar_info_ = bar->getBarInfo();
506
507 // Set the reset interface
508 reset_ = reset;
509
510 // Calculate how many functions are there (f0 is required)
511 std::set<int> functions {0};
512 for (const auto &it : bar_info_) {
513 if (functions.find(it.function) == functions.end()) {
514 function_data_[it.function];
515 functions.insert(it.function);
516 if (it.function > 7) {
517 function_number_has_8bits_ = true;
518 }
519 }
520 }
521
522 // Bind pcie_map_target_socket_ so device can call pcie_map interface
523 auto *initiator = dynamic_cast<
524 tlm::tlm_initiator_socket<BUSWIDTH, TYPES> *>(
526 if (initiator) {
527 pcie_map_target_socket_.bind(*initiator);
528 } else {
529 SIM_LOG_ERROR(o, Log_Configuration,
530 "object returned by getPcieMapInitiatorSocket was not"
531 " a tlm::tlm_initiator_socket.");
532 }
533
534 simulation_obj_ = o;
535 createCfgMapHelper();
536}
537
538template <unsigned int BUSWIDTH, typename TYPES>
539std::map<std::pair<size_t, size_t>, size_t>
541 return address_id_mem_map_;
542}
543
544template <unsigned int BUSWIDTH, typename TYPES>
545std::map<std::pair<size_t, size_t>, size_t>
547 return address_id_io_map_;
548}
549
550template <unsigned int BUSWIDTH, typename TYPES>
552retrieveFunctionData(size_t socket_id, int bar_id) {
553 if (initiator_sockets_.size() <= socket_id) {
554 std::ostringstream os;
555 os << "Invalid socket_id: " << socket_id;
556 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
557 return;
558 }
559
560 tlm::tlm_generic_payload trans;
561 // The data pointer, length and stream width attribute are reused
562 // since they are not modifiable by the target according to the spec
563 uint32_t value = 0;
564 trans.set_data_ptr(reinterpret_cast<unsigned char *>(&value));
565 trans.set_data_length(4);
566 trans.set_streaming_width(4);
567
568 trans.set_command(tlm::TLM_READ_COMMAND);
569 trans.set_address(0x4);
570
571 auto ret = initiator_sockets_[socket_id]->transport_dbg(trans);
572 if (ret == 0) {
573 // The target is not able to perform the transport_dbg.
574 // The PCIe interconnector is unable to get the up-to-date
575 // BAR/CMD information which may cause unexpected behavior
576 return;
577 }
578
579 auto fd = function_data_.begin();
580 std::advance(fd, socket_id);
581 fd->second.command = value;
582
583 // NOTE: the IO, Mem32, Mem64 bits are ignored by updateMapping,
584 // no need to filter them out here
585 for (const auto &bar : bar_info_) {
586 // BarInfo holds _all_ BARs for _all_ functions. This can probably
587 // be done in a much more clever way that avoids iterating over the
588 // entire BarInfo for each function in the outer loop.
589 if (bar.function != fd->first) {
590 continue;
591 }
592
593 int expected_bar_id = bar.offset == 0x30 ? 6 : (bar.offset - 0x10) / 4;
594 // bar_id == -1 => read all bars
595 if (bar_id != -1 && bar_id != expected_bar_id) {
596 continue;
597 }
598
599 trans.set_address(bar.offset);
600 initiator_sockets_[socket_id]->transport_dbg(trans);
601 fd->second.base_address[expected_bar_id] = value;
602 if (bar.is_64bit) {
603 if (expected_bar_id == 6) {
604 SC_REPORT_ERROR(
605 PCIE_MAPPING_INTERCONNECT_TAG,
606 "Expansion ROM BAR can't support 64-bit address");
607 } else {
608 // Read higher 32 bits in next BAR
609 trans.set_address(bar.offset + 4);
610 initiator_sockets_[socket_id]->transport_dbg(trans);
611 fd->second.base_address[expected_bar_id + 1] = value;
612 }
613 }
614 }
615}
616
617template <unsigned int BUSWIDTH, typename TYPES>
618void PcieMappingInterconnect<BUSWIDTH, TYPES>::
619addMap(types::map_info_t info, types::pcie_type_t type) {
620 std::ostringstream os;
621 os << "Adding mapping of type: "
622 << pcieTypeName(type);
623 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
624 pcie_map_extension_.add_map(info, type);
625 if (sender_.failed_transaction_.payload()) {
626 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, "Failure to add map");
627 return;
628 }
629}
630
631template <unsigned int BUSWIDTH, typename TYPES>
632void PcieMappingInterconnect<BUSWIDTH, TYPES>::
633delMap(size_t socket_id, types::pcie_type_t type) {
634 types::map_info_t::physical_address_t base = 0xffffffffffffffff;
635 if (type == types::PCIE_Type_Mem) {
636 auto it = std::find_if(
637 address_id_mem_map_.begin(),
638 address_id_mem_map_.end(),
639 [socket_id](
640 const std::pair<std::pair<size_t, size_t>, size_t> &entry) {
641 return entry.second == socket_id;
642 });
643
644 if (it != address_id_mem_map_.end()) {
645 base = it->first.first;
646 address_id_mem_map_.erase(it);
647 }
648 } else if (type == types::PCIE_Type_IO) {
649 auto it = std::find_if(
650 address_id_io_map_.begin(),
651 address_id_io_map_.end(),
652 [socket_id](
653 const std::pair<std::pair<size_t, size_t>, size_t> &entry) {
654 return entry.second == socket_id;
655 });
656
657 if (it != address_id_io_map_.end()) {
658 base = it->first.first;
659 address_id_io_map_.erase(it);
660 }
661 }
662
663 if (base != 0xffffffffffffffff) {
664 std::ostringstream os;
665 os << "Deleting mapping of type: "
666 << pcieTypeName(type) << ", base: 0x" << std::hex << base;
667 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
668
669 pcie_map_extension_.del_map(base, type);
670 if (sender_.failed_transaction_.payload()) {
671 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, "Failure to del map");
672 return;
673 }
674 }
675}
676
677template <unsigned int BUSWIDTH, typename TYPES>
678void PcieMappingInterconnect<BUSWIDTH, TYPES>::
679updateMappings(int function_id, int bar_id) {
680 for (const auto &bi : bar_info_) {
681 if (function_id != -1 && bi.function != function_id) {
682 continue;
683 }
684
685 auto header = function_data_.at(bi.function);
686
687 int expected_bar_id = bi.offset == 0x30 ? 6 : (bi.offset - 0x10) / 4;
688 if (bar_id != -1 && bar_id != expected_bar_id) {
689 continue;
690 }
691
692 uint64_t base_address = header.base_address[expected_bar_id];
693 if (bi.is_64bit) {
694 if (expected_bar_id == 6) {
695 SC_REPORT_ERROR(
696 PCIE_MAPPING_INTERCONNECT_TAG,
697 "Expansion ROM BAR can't support 64-bit address");
698 } else {
699 base_address |= static_cast<uint64_t>(
700 header.base_address[expected_bar_id + 1]) << 32;
701 }
702 }
703
704 bool enable = header.command & 1;
706 if (bi.is_memory) {
708 if (bi.offset == 0x30) {
709 enable = base_address & 1;
710 } else {
711 enable = (header.command >> 1) & 1;
712 }
713 }
714 base_address &= ~((1ULL << bi.size_bits) - 1);
715 size_t socket_id = fn_bar_id_map_.at(
716 std::make_pair(bi.function, bi.offset));
717 delMap(socket_id, type);
718 if (enable) {
719 // The `start` field in map_info_t is set to base,
720 // so later when receiving the transaction, the address
721 // can be used to route the transaction to the right socket
722 types::map_info_t info(base_address, base_address,
723 1ULL << bi.size_bits, function_id);
724 auto address_range = std::make_pair(info.base,
725 info.base + info.length);
726
727 if (bi.is_memory) {
728 address_id_mem_map_[address_range] = socket_id;
729 std::ostringstream os;
730 os << "Memory address for function " << bi.function
731 << " and bar ID " << expected_bar_id
732 << " in range of [0x" << std::hex << info.base
733 << "-0x" << info.base + info.length << "] maps to socket ID "
734 << socket_id;
735 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
736 } else {
737 address_id_io_map_[address_range] = socket_id;
738 std::ostringstream os;
739 os << "IO address for function " << bi.function
740 << " and bar ID " << expected_bar_id
741 << " in range of [0x" << std::hex << info.base
742 << "-0x" << info.base + info.length << "] maps to socket ID "
743 << socket_id;
744 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
745 }
746 addMap(info, type);
747 }
748 }
749}
750
751template <unsigned int BUSWIDTH, typename TYPES>
752void PcieMappingInterconnect<BUSWIDTH, TYPES>::
753updateCommand(uint16_t function_id, uint16_t old_command) {
754 if (function_data_[function_id].command != old_command) {
755 updateMappings(function_id);
756 }
757}
758
759template <unsigned int BUSWIDTH, typename TYPES>
760void PcieMappingInterconnect<BUSWIDTH, TYPES>::
761interceptCfgHeaderAccess(size_t socket_id,
762 tlm::tlm_generic_payload &trans) { // NOLINT
763 unsigned int size = trans.get_data_length();
764 // Check assumptions (but leave it up to target device to report errors)
765 if (trans.get_byte_enable_ptr() || trans.get_streaming_width() != size) {
766 return;
767 }
768
769 // We are only interested in write access to BARn or Command regs
770 if (!trans.is_write()) {
771 return;
772 }
773
774 // Maximum size is 8
775 uint64_t *data = reinterpret_cast<uint64_t *>(trans.get_data_ptr());
776
777 auto it = function_data_.begin();
778 std::advance(it, socket_id);
779 uint16_t function_id = it->first;
780 auto &header = it->second;
781 sc_dt::uint64 offset = trans.get_address();
782 switch (offset) {
783 case 0x04: { // command & status
784 uint16_t old_command = header.command;
785 retrieveFunctionData(socket_id, -1);
786 header.command = (*data) & 0xffff; // ignore status
787 updateCommand(function_id, old_command);
788 break;
789 }
790 case 0x10: // BAR0-BAR5
791 case 0x14:
792 case 0x18:
793 case 0x1c:
794 case 0x20:
795 case 0x24: {
796 int bar_id = (offset - 0x10) / 4;
797 retrieveFunctionData(socket_id, bar_id);
798 // NOTE: the IO, Mem32, Mem64 bits are ignored by
799 // updateMapping, no need to filter them out here. In
800 // addition, any bits less than the size of the BAR as defined
801 // by the BarInfo will also be ignored/truncated by
802 // updateMapping; making sure we always send a naturally
803 // aligned address to Simics
804 header.base_address[bar_id] = *data & 0xffffffff;
805 if (size == 8 && !(offset % 8)) {
806 header.base_address[bar_id + 1] = *data >> 32;
807 }
808 updateMappings(function_id, bar_id);
809 break;
810 }
811 case 0x30: // Expansion ROM BAR
812 int bar_id = 6;
813 retrieveFunctionData(socket_id, bar_id);
814 header.base_address[bar_id] = *data & 0xffffffff;
815 updateMappings(function_id, bar_id);
816 break;
817 }
818}
819
820template <unsigned int BUSWIDTH, typename TYPES>
821size_t PcieMappingInterconnect<BUSWIDTH, TYPES>::
822findSocketId(types::pcie_type_t type, uint16_t function_id,
823 tlm::tlm_generic_payload &trans) const { // NOLINT
824 if (type == types::PCIE_Type_Cfg) {
825 auto found = function_data_.find(function_id);
826 if (found == function_data_.end()) {
827 std::ostringstream os;
828 os << "Function id(" << function_id
829 << ") not found for this device";
830 throw std::invalid_argument {
831 os.str()
832 };
833 }
834 return std::distance(function_data_.begin(), found);
835 } else if (type == types::PCIE_Type_Mem) {
836 size_t address = trans.get_address();
837 for (const auto &entry : address_id_mem_map_) {
838 auto startAddress = entry.first.first;
839 auto endAddress = entry.first.second;
840 auto id = entry.second;
841
842 if (address >= startAddress && address < endAddress) {
843 if (enable_base_address_subtraction) {
844 trans.set_address(address - startAddress);
845 }
846 return id;
847 }
848 }
849 throw std::invalid_argument {
850 "Unable to find the initiator_socket to forward the MEM transaction"
851 };
852 } else if (type == types::PCIE_Type_IO) {
853 size_t address = trans.get_address();
854 for (const auto &entry : address_id_io_map_) {
855 auto startAddress = entry.first.first;
856 auto endAddress = entry.first.second;
857 auto id = entry.second;
858
859 if (address >= startAddress && address < endAddress) {
860 if (enable_base_address_subtraction) {
861 trans.set_address(address - startAddress);
862 }
863 return id;
864 }
865 }
866 throw std::invalid_argument {
867 "Unable to find the initiator_socket to forward the IO transaction"
868 };
869 } else if (type == types::PCIE_Type_Msg) {
870 return function_data_.size() + bar_info_.size();
871 }
872 throw std::invalid_argument {
873 "Unsupported PCIe type"
874 };
875}
876
877template <unsigned int BUSWIDTH, typename TYPES>
880 auto *map_helper = SIM_get_class("pcie_map_helper_cpp");
881 if (map_helper == nullptr) {
882 SIM_LOG_ERROR(
883 simulation_obj_, Log_Configuration,
884 "Simics class pcie_map_helper_cpp is required."
885 "Try load the module pcie-map-helper-c++ first.");
886 return;
887 }
888
889 if (SIM_c_get_interface(simulation_obj_, "pcie_device") == nullptr) {
890 // Make sure simulation_obj_ is ready to be used by other objects
891 if (!SIM_object_is_configured(simulation_obj_)) {
892 SIM_LOG_ERROR(
893 simulation_obj_, Log_Configuration,
894 "Simulation object is not configured yet, cannot get"
895 " its gasket_list attribute");
896 return;
897 }
898
899 // For PCIe gasket object, the interface is not implemented
900 // on the simulation object, but rather the gasket object
901 auto gasket_list = SIM_get_attribute(simulation_obj_, "gasket_list");
902 int gasket_count = SIM_attr_list_size(gasket_list);
903 for (int i = 0; i < gasket_count; ++i) {
904 auto obj = SIM_attr_object(SIM_attr_list_item(gasket_list, i));
905 if (SIM_c_get_interface(obj, "pcie_device")) {
906 gasket_obj_ = obj;
907 break;
908 }
909 }
910 if (gasket_obj_.object() == nullptr) {
911 SIM_LOG_ERROR(
912 simulation_obj_, Log_Configuration,
913 "No gasket object implemented the required"
914 " pcie_device interface");
915 return;
916 }
917 } else {
918 gasket_obj_ = simulation_obj_.object();
919 }
920
921 for (const auto &fd : function_data_) {
922 auto pcie_type = std_to_attr<>(
923 std::pair<std::string, int>("pcie_type", types::PCIE_Type_Cfg));
924 auto forward_target = std_to_attr<>(
925 std::pair<std::string, ConfObjectRef>("forward_target",
926 gasket_obj_));
927 auto function_id = std_to_attr<>(
928 std::pair<std::string, int>("function_id", fd.first));
929 attr_value_t attr = SIM_make_attr_list(3, pcie_type, forward_target,
930 function_id);
931
932 auto obj_name = gasket_obj_.name() + ".port.cfg" + \
933 std::to_string(fd.first);
934 auto *o = SIM_get_object(obj_name.c_str());
935 if (o == nullptr) {
936 std::ignore = SIM_clear_exception();
937 o = SIM_create_object(map_helper, obj_name.c_str(), attr);
938 }
939 assert(o);
940 SIM_attr_free(&attr);
941 }
942}
943
944template <unsigned int BUSWIDTH, typename TYPES>
946getCfgMapHelper(uint16_t device_id) {
947 std::string name = "port.cfg";
948 if (function_number_has_8bits_) {
949 name += std::to_string(device_id & 0xff);
950 } else {
951 name += std::to_string(device_id & 7);
952 }
953 auto map_helper = SIM_object_descendant(gasket_obj_, name.c_str());
954 if (!map_helper) {
955 SIM_LOG_CRITICAL(simulation_obj_, Log_Configuration,
956 "Could not find map helper object with name '%s.%s'",
957 gasket_obj_.name().c_str(), name.c_str());
958 }
959 return map_helper;
960}
961
962template <unsigned int BUSWIDTH, typename TYPES>
963size_t PcieMappingInterconnect<BUSWIDTH, TYPES>::
964validateTransaction(tlm::tlm_generic_payload &trans) { // NOLINT
965 PcieTlmExtension *pcie_ext = nullptr;
966 trans.get_extension<PcieTlmExtension>(pcie_ext);
967
968 if (pcie_ext == nullptr) {
969 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
970 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
971 "PcieTlmExtension is required but not found");
972 return 0;
973 }
974
975 std::ostringstream os;
976 os << "Received a PCIe transaction with type "
977 << pcieTypeName(pcie_ext->type);
978 if (pcie_ext->device_id_set) {
979 os << ", " << pcieDeviceIdStr(pcie_ext->device_id);
980 }
981 os << ", address 0x" << std::hex << trans.get_address() << std::endl;
982 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
983
984 if (pcie_ext->type == types::PCIE_Type_Not_Set
985 || pcie_ext->type == types::PCIE_Type_Other) {
986 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
987 "Only support following PCIe types: MEM/IO/CFG/MSG");
988 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
989 return 0;
990 }
991
992 if (pcie_ext->type == types::PCIE_Type_Cfg
993 && !pcie_ext->device_id_set) {
994 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
995 "PCIe device ID ATOM is required");
996 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
997 return 0;
998 }
999
1000 size_t socket_id = 0;
1001 try {
1002 socket_id = findSocketId(pcie_ext->type, pcie_ext->device_id & 7,
1003 trans);
1004 } catch (const std::exception &e) {
1005 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, e.what());
1006 trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
1007 return 0;
1008 }
1009
1010 if (socket_id >= initiator_sockets_.size()) {
1011 os.clear();
1012 os << "Initiator socket ID " << socket_id << " not created yet";
1013 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
1014 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
1015 return 0;
1016 }
1017
1018 if (pcie_ext->type == types::PCIE_Type_Cfg) {
1019 interceptCfgHeaderAccess(socket_id, trans);
1020 }
1021
1022 return socket_id;
1023}
1024
1025template <unsigned int BUSWIDTH, typename TYPES>
1026void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1027transaction_b_transport(tlm::tlm_generic_payload &trans, // NOLINT: SystemC API
1028 sc_core::sc_time &t) { // NOLINT: SystemC API
1029 trans.set_response_status(tlm::TLM_OK_RESPONSE);
1030 size_t socket_id = validateTransaction(trans);
1031 if (trans.get_response_status() == tlm::TLM_OK_RESPONSE) {
1032 initiator_sockets_.at(socket_id)->b_transport(trans, t);
1033 }
1034}
1035
1036template <unsigned int BUSWIDTH, typename TYPES>
1037unsigned int PcieMappingInterconnect<BUSWIDTH, TYPES>::
1038transaction_transport_dbg(tlm::tlm_generic_payload &trans) { // NOLINT
1039 trans.set_response_status(tlm::TLM_OK_RESPONSE);
1040 size_t socket_id = validateTransaction(trans);
1041 if (trans.get_response_status() == tlm::TLM_OK_RESPONSE) {
1042 return initiator_sockets_.at(socket_id)->transport_dbg(trans);
1043 } else {
1044 return 0;
1045 }
1046}
1047
1048/*
1049 * For pcie-map, just relay the incoming BW transaction to target socket
1050 */
1051template <unsigned int BUSWIDTH, typename TYPES>
1052void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1053pcie_map_invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
1054 sc_dt::uint64 end_range) {
1055 pcie_map_target_socket_->invalidate_direct_mem_ptr(start_range,
1056 end_range);
1057}
1058
1059/*
1060 * For pcie-map, just relay the incoming transaction to outgoing socket
1061 */
1062template <unsigned int BUSWIDTH, typename TYPES>
1063void PcieMappingInterconnect<BUSWIDTH, TYPES>::pcie_map_b_transport(
1064 tlm::tlm_generic_payload &trans, // NOLINT
1065 sc_core::sc_time &t) { // NOLINT
1066 pcie_map_initiator_socket->b_transport(trans, t);
1067}
1068
1069/*
1070 * For pcie-map, just relay the incoming transaction to outgoing socket
1071 */
1072template <unsigned int BUSWIDTH, typename TYPES>
1073unsigned int PcieMappingInterconnect<BUSWIDTH, TYPES>::pcie_map_transport_dbg(
1074 tlm::tlm_generic_payload &trans) { // NOLINT
1075 return pcie_map_initiator_socket->transport_dbg(trans);
1076}
1077
1078/*
1079 * For pcie-map, just relay the incoming transaction to outgoing socket
1080 */
1081template <unsigned int BUSWIDTH, typename TYPES>
1082bool PcieMappingInterconnect<BUSWIDTH, TYPES>::pcie_map_get_direct_mem_ptr(
1083 tlm::tlm_generic_payload &trans, // NOLINT
1084 tlm::tlm_dmi &dmi_data) { // NOLINT
1085 return pcie_map_initiator_socket->get_direct_mem_ptr(trans, dmi_data);
1086}
1087
1088template <unsigned int BUSWIDTH, typename TYPES>
1089void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1090castToTarget(sc_core::sc_object *object, supported_target_socket_t *target) {
1091 if (object == nullptr) {
1092 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
1093 "Unable to castToTarget from a nullptr");
1094 return;
1095 }
1096
1097 auto *tlm_target = dynamic_cast<
1098 tlm::tlm_target_socket<BUSWIDTH, TYPES> *>(object);
1099 if (tlm_target) {
1100 target->tlm = tlm_target;
1101 return;
1102 }
1103
1104 auto *multi_target = dynamic_cast<
1105 tlm_utils::multi_target_base<BUSWIDTH, TYPES> *>(object);
1106 if (multi_target) {
1107 target->multi = multi_target;
1108 return;
1109 }
1110
1111 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
1112 "Unable to dynamic-cast PCIe target socket");
1113}
1114
1115} // namespace composite
1116} // namespace systemc
1117} // namespace simics
1118
1119#endif
void send_extension(iface::Transaction *transaction) override
Called by extension after the extension is set on the payload.
Definition: pcie_mapping_interconnect.h:87
virtual ~PcieMappingInterconnectExtensionSender()
Definition: pcie_mapping_interconnect.h:84
iface::Transaction failed_transaction_
Definition: pcie_mapping_interconnect.h:96
void send_failed(iface::Transaction *transaction) override
Called by extension if method_call invocation was missing.
Definition: pcie_mapping_interconnect.h:91
Definition: pcie_mapping_interconnect.h:106
PcieMappingInterconnect(sc_core::sc_module_name="PcieMappingInterconnect")
Definition: pcie_mapping_interconnect.h:121
void connected(uint16_t device_id)
Definition: pcie_mapping_interconnect.h:377
void before_end_of_elaboration() override
Definition: pcie_mapping_interconnect.h:334
bool enable_base_address_subtraction
Definition: pcie_mapping_interconnect.h:203
initiator_socket_t pcie_map_initiator_socket
IC -> Simics, forwards the upstream pcie-map transactions.
Definition: pcie_mapping_interconnect.h:198
void hotReset()
Definition: pcie_mapping_interconnect.h:407
void createCfgMapHelper()
Definition: pcie_mapping_interconnect.h:879
target_socket_t pcie_device_target_socket
Definition: pcie_mapping_interconnect.h:196
std::map< std::pair< size_t, size_t >, size_t > addressIdMemMap() const
Definition: pcie_mapping_interconnect.h:540
void connect(iface::PcieDeviceQueryInterface *pci, iface::PcieBaseAddressRegisterQueryInterface *bar, iface::PcieResetInterface *reset, ConfObjectRef o)
Called by (outer) composite class to retrieve the required information to connect the IC with the PCI...
Definition: pcie_mapping_interconnect.h:474
target_socket_t transaction_target_socket
Definition: pcie_mapping_interconnect.h:194
void disconnected(uint16_t device_id)
Definition: pcie_mapping_interconnect.h:397
sc_core::sc_in< bool > warm_reset_pin
Definition: pcie_mapping_interconnect.h:200
std::map< std::pair< size_t, size_t >, size_t > addressIdIoMap() const
Definition: pcie_mapping_interconnect.h:546
Generic extension sender initialized with a TLM2 initiator socket of TSocket type.
Definition: extension_sender.h:35
void send_extension(Transaction *transaction) override
Called by extension after the extension is set on the payload.
Definition: extension_sender.h:55
void init(TSocket *socket)
Definition: extension_sender.h:40
Transaction transaction() override
Called by extension to get a new Transaction.
Definition: extension_sender.h:49
void send_failed(Transaction *transaction) override
Called by extension if method_call invocation was missing.
Definition: extension_sender.h:58
void init(ExtensionSenderInterface *sender, tlm::tlm_generic_payload *payload)
Deprecated, use the init(ExtensionSenderInterface *sender) instead.
Definition: extension.h:45
Interface that allows the Simics glue to perform snooping and automatic connection of the device's ta...
Definition: pcie_device_query_interface.h:50
virtual std::vector< PCIeBar > getBarInfo()=0
BAR register information.
Interface required from a SystemC PCIe device in order to connect to Simics.
Definition: pcie_device_query_interface.h:29
virtual sc_core::sc_object * getMsgTargetSocket()=0
virtual sc_core::sc_object * getConfigTargetSocket()=0
virtual sc_core::sc_object * getPcieMapInitiatorSocket()=0
Extension for Simics pcie_map interface.
Definition: pcie_map_extension.h:28
Definition: pcie_device_query_interface.h:92
Class that encapsulates a generic_payload and returns it to the TransactionPool when the Transaction ...
Definition: transaction.h:31
conf_object_t * SIM_create_object(conf_class_t *NOTNULL cls, const char *name, attr_value_t attrs)
conf_object_t * SIM_get_object(const char *NOTNULL name)
attr_value_t SIM_get_attribute(conf_object_t *NOTNULL obj, const char *name)
conf_class_t * SIM_get_class(const char *NOTNULL name)
@ Log_Configuration
Definition: adapter_log_groups.h:25
pcie_type_t
Definition: pcie_type.h:22
@ PCIE_Type_Cfg
Definition: pcie_type.h:26
@ PCIE_Type_Mem
Definition: pcie_type.h:24
@ PCIE_Type_Msg
Definition: pcie_type.h:27
@ PCIE_Type_Not_Set
Definition: pcie_type.h:23
@ PCIE_Type_Other
Definition: pcie_type.h:28
@ PCIE_Type_IO
Definition: pcie_type.h:25
Definition: adapter.h:81
Reduced, stand-alone, version of the Simics map_info_t struct.
Definition: map_info.h:25
uint64_t physical_address_t
Definition: map_info.h:26