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
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 pcie_map_extension_.add_function(getCfgMapHelper(device_id), device_id);
438 if (sender_.failed_transaction_.payload()) {
439 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
440 "Failure to add function");
441 return;
442 }
443}
444
445template <unsigned int BUSWIDTH, typename TYPES>
446void PcieMappingInterconnect<BUSWIDTH, TYPES>::
447delFunction(uint16_t device_id) {
448 std::ostringstream os;
449 os << "Deleting function for device_id: "
450 << device_id;
451 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
452 pcie_map_extension_.del_function(getCfgMapHelper(device_id), device_id);
453 if (sender_.failed_transaction_.payload()) {
454 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
455 "Failure to delete function");
456 return;
457 }
458}
459
460template <unsigned int BUSWIDTH, typename TYPES>
464 iface::PcieResetInterface *reset, ConfObjectRef o) {
465 if (pci == nullptr) {
466 SIM_LOG_ERROR(
468 "PcieDeviceQueryInterface not implemented on the device");
469 return;
470 }
471
472 if (bar == nullptr) {
473 SIM_LOG_ERROR(
475 "PcieBaseAddressRegisterQueryInterface not implemented on"
476 " the device");
477 return;
478 }
479
480 if (reset == nullptr) {
481 SIM_LOG_ERROR(o, Log_Configuration,
482 "PcieResetInterface not implemented on the device");
483 return;
484 }
485
486 // Get the config target_socket from the device
487 castToTarget(pci->getConfigTargetSocket(), &config_target_socket_);
488
489 // Get the message target_socket from the device
490 castToTarget(pci->getMsgTargetSocket(), &msg_target_socket_);
491
492 // Get the BAR info
493 bar_info_ = bar->getBarInfo();
494
495 // Set the reset interface
496 reset_ = reset;
497
498 // Calculate how many functions are there (f0 is required)
499 std::set<int> functions {0};
500 for (const auto &it : bar_info_) {
501 if (functions.find(it.function) == functions.end()) {
502 function_data_[it.function];
503 functions.insert(it.function);
504 if (it.function > 7) {
505 function_number_has_8bits_ = true;
506 }
507 }
508 }
509
510 // Bind pcie_map_target_socket_ so device can call pcie_map interface
511 auto *initiator = dynamic_cast<
512 tlm::tlm_initiator_socket<BUSWIDTH, TYPES> *>(
514 if (initiator) {
515 pcie_map_target_socket_.bind(*initiator);
516 } else {
517 SIM_LOG_ERROR(o, Log_Configuration,
518 "object returned by getPcieMapInitiatorSocket was not"
519 " a tlm::tlm_initiator_socket.");
520 }
521
522 simulation_obj_ = o;
523 createCfgMapHelper();
524}
525
526template <unsigned int BUSWIDTH, typename TYPES>
527std::map<std::pair<size_t, size_t>, size_t>
529 return address_id_mem_map_;
530}
531
532template <unsigned int BUSWIDTH, typename TYPES>
533std::map<std::pair<size_t, size_t>, size_t>
535 return address_id_io_map_;
536}
537
538template <unsigned int BUSWIDTH, typename TYPES>
540retrieveFunctionData(size_t socket_id, int bar_id) {
541 if (initiator_sockets_.size() <= socket_id) {
542 std::ostringstream os;
543 os << "Invalid socket_id: " << socket_id;
544 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
545 return;
546 }
547
548 tlm::tlm_generic_payload trans;
549 // The data pointer, length and stream width attribute are reused
550 // since they are not modifiable by the target according to the spec
551 uint32_t value = 0;
552 trans.set_data_ptr(reinterpret_cast<unsigned char *>(&value));
553 trans.set_data_length(4);
554 trans.set_streaming_width(4);
555
556 trans.set_command(tlm::TLM_READ_COMMAND);
557 trans.set_address(0x4);
558
559 auto ret = initiator_sockets_[socket_id]->transport_dbg(trans);
560 if (ret == 0) {
561 // The target is not able to perform the transport_dbg.
562 // The PCIe interconnector is unable to get the up-to-date
563 // BAR/CMD information which may cause unexpected behavior
564 return;
565 }
566
567 auto fd = function_data_.begin();
568 std::advance(fd, socket_id);
569 fd->second.command = value;
570
571 // NOTE: the IO, Mem32, Mem64 bits are ignored by updateMapping,
572 // no need to filter them out here
573 for (const auto &bar : bar_info_) {
574 // BarInfo holds _all_ BARs for _all_ functions. This can probably
575 // be done in a much more clever way that avoids iterating over the
576 // entire BarInfo for each function in the outer loop.
577 if (bar.function != fd->first) {
578 continue;
579 }
580
581 int expected_bar_id = bar.offset == 0x30 ? 6 : (bar.offset - 0x10) / 4;
582 // bar_id == -1 => read all bars
583 if (bar_id != -1 && bar_id != expected_bar_id) {
584 continue;
585 }
586
587 trans.set_address(bar.offset);
588 initiator_sockets_[socket_id]->transport_dbg(trans);
589 fd->second.base_address[expected_bar_id] = value;
590 if (bar.is_64bit) {
591 if (expected_bar_id == 6) {
592 SC_REPORT_ERROR(
593 PCIE_MAPPING_INTERCONNECT_TAG,
594 "Expansion ROM BAR can't support 64-bit address");
595 } else {
596 // Read higher 32 bits in next BAR
597 trans.set_address(bar.offset + 4);
598 initiator_sockets_[socket_id]->transport_dbg(trans);
599 fd->second.base_address[expected_bar_id + 1] = value;
600 }
601 }
602 }
603}
604
605template <unsigned int BUSWIDTH, typename TYPES>
606void PcieMappingInterconnect<BUSWIDTH, TYPES>::
607addMap(types::map_info_t info, types::pcie_type_t type) {
608 std::ostringstream os;
609 os << "Adding mapping of type: "
610 << pcieTypeName(type);
611 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
612 pcie_map_extension_.add_map(info, type);
613 if (sender_.failed_transaction_.payload()) {
614 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, "Failure to add map");
615 return;
616 }
617}
618
619template <unsigned int BUSWIDTH, typename TYPES>
620void PcieMappingInterconnect<BUSWIDTH, TYPES>::
621delMap(size_t socket_id, types::pcie_type_t type) {
622 types::map_info_t::physical_address_t base = 0xffffffffffffffff;
623 if (type == types::PCIE_Type_Mem) {
624 auto it = std::find_if(
625 address_id_mem_map_.begin(),
626 address_id_mem_map_.end(),
627 [socket_id](
628 const std::pair<std::pair<size_t, size_t>, size_t> &entry) {
629 return entry.second == socket_id;
630 });
631
632 if (it != address_id_mem_map_.end()) {
633 base = it->first.first;
634 address_id_mem_map_.erase(it);
635 }
636 } else if (type == types::PCIE_Type_IO) {
637 auto it = std::find_if(
638 address_id_io_map_.begin(),
639 address_id_io_map_.end(),
640 [socket_id](
641 const std::pair<std::pair<size_t, size_t>, size_t> &entry) {
642 return entry.second == socket_id;
643 });
644
645 if (it != address_id_io_map_.end()) {
646 base = it->first.first;
647 address_id_io_map_.erase(it);
648 }
649 }
650
651 if (base != 0xffffffffffffffff) {
652 std::ostringstream os;
653 os << "Deleting mapping of type: "
654 << pcieTypeName(type) << ", base: 0x" << std::hex << base;
655 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
656
657 pcie_map_extension_.del_map(base, type);
658 if (sender_.failed_transaction_.payload()) {
659 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, "Failure to del map");
660 return;
661 }
662 }
663}
664
665template <unsigned int BUSWIDTH, typename TYPES>
666void PcieMappingInterconnect<BUSWIDTH, TYPES>::
667updateMappings(int function_id, int bar_id) {
668 for (const auto &bi : bar_info_) {
669 if (function_id != -1 && bi.function != function_id) {
670 continue;
671 }
672
673 auto header = function_data_.at(bi.function);
674
675 int expected_bar_id = bi.offset == 0x30 ? 6 : (bi.offset - 0x10) / 4;
676 if (bar_id != -1 && bar_id != expected_bar_id) {
677 continue;
678 }
679
680 uint64_t base_address = header.base_address[expected_bar_id];
681 if (bi.is_64bit) {
682 if (expected_bar_id == 6) {
683 SC_REPORT_ERROR(
684 PCIE_MAPPING_INTERCONNECT_TAG,
685 "Expansion ROM BAR can't support 64-bit address");
686 } else {
687 base_address |= static_cast<uint64_t>(
688 header.base_address[expected_bar_id + 1]) << 32;
689 }
690 }
691
692 bool enable = header.command & 1;
694 if (bi.is_memory) {
696 if (bi.offset == 0x30) {
697 enable = base_address & 1;
698 } else {
699 enable = (header.command >> 1) & 1;
700 }
701 }
702 base_address &= ~((1ULL << bi.size_bits) - 1);
703 size_t socket_id = fn_bar_id_map_.at(
704 std::make_pair(bi.function, bi.offset));
705 delMap(socket_id, type);
706 if (enable) {
707 // The `start` field in map_info_t is set to base,
708 // so later when receiving the transaction, the address
709 // can be used to route the transaction to the right socket
710 types::map_info_t info(base_address, base_address,
711 1ULL << bi.size_bits, function_id);
712 auto address_range = std::make_pair(info.base,
713 info.base + info.length);
714
715 if (bi.is_memory) {
716 address_id_mem_map_[address_range] = socket_id;
717 std::ostringstream os;
718 os << "Memory address for function " << bi.function
719 << " and bar ID " << expected_bar_id
720 << " in range of [0x" << std::hex << info.base
721 << "-0x" << info.base + info.length << "] maps to socket ID "
722 << socket_id;
723 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
724 } else {
725 address_id_io_map_[address_range] = socket_id;
726 std::ostringstream os;
727 os << "IO address for function " << bi.function
728 << " and bar ID " << expected_bar_id
729 << " in range of [0x" << std::hex << info.base
730 << "-0x" << info.base + info.length << "] maps to socket ID "
731 << socket_id;
732 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
733 }
734 addMap(info, type);
735 }
736 }
737}
738
739template <unsigned int BUSWIDTH, typename TYPES>
740void PcieMappingInterconnect<BUSWIDTH, TYPES>::
741updateCommand(uint16_t function_id, uint16_t old_command) {
742 if (function_data_[function_id].command != old_command) {
743 updateMappings(function_id);
744 }
745}
746
747template <unsigned int BUSWIDTH, typename TYPES>
748void PcieMappingInterconnect<BUSWIDTH, TYPES>::
749interceptCfgHeaderAccess(size_t socket_id,
750 tlm::tlm_generic_payload &trans) { // NOLINT
751 unsigned int size = trans.get_data_length();
752 // Check assumptions (but leave it up to target device to report errors)
753 if (trans.get_byte_enable_ptr() || trans.get_streaming_width() != size) {
754 return;
755 }
756
757 // We are only interested in write access to BARn or Command regs
758 if (!trans.is_write()) {
759 return;
760 }
761
762 // Maximum size is 8
763 uint64_t *data = reinterpret_cast<uint64_t *>(trans.get_data_ptr());
764
765 auto it = function_data_.begin();
766 std::advance(it, socket_id);
767 uint16_t function_id = it->first;
768 auto &header = it->second;
769 sc_dt::uint64 offset = trans.get_address();
770 switch (offset) {
771 case 0x04: { // command & status
772 uint16_t old_command = header.command;
773 retrieveFunctionData(socket_id, -1);
774 header.command = (*data) & 0xffff; // ignore status
775 updateCommand(function_id, old_command);
776 break;
777 }
778 case 0x10: // BAR0-BAR5
779 case 0x14:
780 case 0x18:
781 case 0x1c:
782 case 0x20:
783 case 0x24: {
784 int bar_id = (offset - 0x10) / 4;
785 retrieveFunctionData(socket_id, bar_id);
786 // NOTE: the IO, Mem32, Mem64 bits are ignored by
787 // updateMapping, no need to filter them out here. In
788 // addition, any bits less than the size of the BAR as defined
789 // by the BarInfo will also be ignored/truncated by
790 // updateMapping; making sure we always send a naturally
791 // aligned address to Simics
792 header.base_address[bar_id] = *data & 0xffffffff;
793 if (size == 8 && !(offset % 8)) {
794 header.base_address[bar_id + 1] = *data >> 32;
795 }
796 updateMappings(function_id, bar_id);
797 break;
798 }
799 case 0x30: // Expansion ROM BAR
800 int bar_id = 6;
801 retrieveFunctionData(socket_id, bar_id);
802 header.base_address[bar_id] = *data & 0xffffffff;
803 updateMappings(function_id, bar_id);
804 break;
805 }
806}
807
808template <unsigned int BUSWIDTH, typename TYPES>
809size_t PcieMappingInterconnect<BUSWIDTH, TYPES>::
810findSocketId(types::pcie_type_t type, uint16_t function_id,
811 tlm::tlm_generic_payload &trans) const { // NOLINT
812 if (type == types::PCIE_Type_Cfg) {
813 auto found = function_data_.find(function_id);
814 if (found == function_data_.end()) {
815 std::ostringstream os;
816 os << "Function id(" << function_id
817 << ") not found for this device";
818 throw std::invalid_argument {
819 os.str()
820 };
821 }
822 return std::distance(function_data_.begin(), found);
823 } else if (type == types::PCIE_Type_Mem) {
824 size_t address = trans.get_address();
825 for (const auto &entry : address_id_mem_map_) {
826 auto startAddress = entry.first.first;
827 auto endAddress = entry.first.second;
828 auto id = entry.second;
829
830 if (address >= startAddress && address < endAddress) {
831 if (enable_base_address_subtraction) {
832 trans.set_address(address - startAddress);
833 }
834 return id;
835 }
836 }
837 throw std::invalid_argument {
838 "Unable to find the initiator_socket to forward the MEM transaction"
839 };
840 } else if (type == types::PCIE_Type_IO) {
841 size_t address = trans.get_address();
842 for (const auto &entry : address_id_io_map_) {
843 auto startAddress = entry.first.first;
844 auto endAddress = entry.first.second;
845 auto id = entry.second;
846
847 if (address >= startAddress && address < endAddress) {
848 if (enable_base_address_subtraction) {
849 trans.set_address(address - startAddress);
850 }
851 return id;
852 }
853 }
854 throw std::invalid_argument {
855 "Unable to find the initiator_socket to forward the IO transaction"
856 };
857 } else if (type == types::PCIE_Type_Msg) {
858 return function_data_.size() + bar_info_.size();
859 }
860 throw std::invalid_argument {
861 "Unsupported PCIe type"
862 };
863}
864
865template <unsigned int BUSWIDTH, typename TYPES>
868 auto *map_helper = SIM_get_class("pcie_map_helper_cpp");
869 if (map_helper == nullptr) {
870 SIM_LOG_ERROR(
871 simulation_obj_, Log_Configuration,
872 "Simics class pcie_map_helper_cpp is required."
873 "Try load the module pcie-map-helper-c++ first.");
874 return;
875 }
876
877 if (SIM_c_get_interface(simulation_obj_, "pcie_device") == nullptr) {
878 // Make sure simulation_obj_ is ready to be used by other objects
879 if (!SIM_object_is_configured(simulation_obj_)) {
880 SIM_LOG_ERROR(
881 simulation_obj_, Log_Configuration,
882 "Simulation object is not configured yet, cannot get"
883 " its gasket_list attribute");
884 return;
885 }
886
887 // For PCIe gasket object, the interface is not implemented
888 // on the simulation object, but rather the gasket object
889 auto gasket_list = SIM_get_attribute(simulation_obj_, "gasket_list");
890 int gasket_count = SIM_attr_list_size(gasket_list);
891 for (int i = 0; i < gasket_count; ++i) {
892 auto obj = SIM_attr_object(SIM_attr_list_item(gasket_list, i));
893 if (SIM_c_get_interface(obj, "pcie_device")) {
894 gasket_obj_ = obj;
895 break;
896 }
897 }
898 if (gasket_obj_.object() == nullptr) {
899 SIM_LOG_ERROR(
900 simulation_obj_, Log_Configuration,
901 "No gasket object implemented the required"
902 " pcie_device interface");
903 return;
904 }
905 } else {
906 gasket_obj_ = simulation_obj_.object();
907 }
908
909 for (const auto &fd : function_data_) {
910 auto pcie_type = std_to_attr<>(
911 std::pair<std::string, int>("pcie_type", types::PCIE_Type_Cfg));
912 auto forward_target = std_to_attr<>(
913 std::pair<std::string, ConfObjectRef>("forward_target",
914 gasket_obj_));
915 auto function_id = std_to_attr<>(
916 std::pair<std::string, int>("function_id", fd.first));
917 attr_value_t attr = SIM_make_attr_list(3, pcie_type, forward_target,
918 function_id);
919
920 auto obj_name = gasket_obj_.name() + ".port.cfg" + \
921 std::to_string(fd.first);
922 auto *o = SIM_get_object(obj_name.c_str());
923 if (o == nullptr) {
924 (void)SIM_clear_exception();
925 o = SIM_create_object(map_helper, obj_name.c_str(), attr);
926 }
927 assert(o);
928 SIM_attr_free(&attr);
929 }
930}
931
932template <unsigned int BUSWIDTH, typename TYPES>
934getCfgMapHelper(uint16_t device_id) {
935 std::string name = "port.cfg";
936 if (function_number_has_8bits_) {
937 name += std::to_string(device_id & 0xff);
938 } else {
939 name += std::to_string(device_id & 7);
940 }
941 auto map_helper = SIM_object_descendant(gasket_obj_, name.c_str());
942 assert(map_helper);
943 return map_helper;
944}
945
946template <unsigned int BUSWIDTH, typename TYPES>
947size_t PcieMappingInterconnect<BUSWIDTH, TYPES>::
948validateTransaction(tlm::tlm_generic_payload &trans) { // NOLINT
949 PcieTlmExtension *pcie_ext = nullptr;
950 trans.get_extension<PcieTlmExtension>(pcie_ext);
951
952 if (pcie_ext == nullptr) {
953 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
954 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
955 "PcieTlmExtension is required but not found");
956 return 0;
957 }
958
959 std::ostringstream os;
960 os << "Received a PCIe transaction with type "
961 << pcieTypeName(pcie_ext->type);
962 if (pcie_ext->device_id_set) {
963 os << ", " << pcieDeviceIdStr(pcie_ext->device_id);
964 }
965 os << ", address 0x" << std::hex << trans.get_address() << std::endl;
966 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
967
968 if (pcie_ext->type == types::PCIE_Type_Not_Set
969 || pcie_ext->type == types::PCIE_Type_Other) {
970 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
971 "Only support following PCIe types: MEM/IO/CFG/MSG");
972 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
973 return 0;
974 }
975
976 if (pcie_ext->type == types::PCIE_Type_Cfg
977 && !pcie_ext->device_id_set) {
978 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
979 "PCIe device ID ATOM is required");
980 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
981 return 0;
982 }
983
984 size_t socket_id = 0;
985 try {
986 socket_id = findSocketId(pcie_ext->type, pcie_ext->device_id & 7,
987 trans);
988 } catch (const std::exception &e) {
989 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, e.what());
990 trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
991 return 0;
992 }
993
994 if (socket_id >= initiator_sockets_.size()) {
995 os.clear();
996 os << "Initiator socket ID " << socket_id << " not created yet";
997 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
998 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
999 return 0;
1000 }
1001
1002 if (pcie_ext->type == types::PCIE_Type_Cfg) {
1003 interceptCfgHeaderAccess(socket_id, trans);
1004 }
1005
1006 return socket_id;
1007}
1008
1009template <unsigned int BUSWIDTH, typename TYPES>
1010void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1011transaction_b_transport(tlm::tlm_generic_payload &trans, // NOLINT: SystemC API
1012 sc_core::sc_time &t) { // NOLINT: SystemC API
1013 trans.set_response_status(tlm::TLM_OK_RESPONSE);
1014 size_t socket_id = validateTransaction(trans);
1015 if (trans.get_response_status() == tlm::TLM_OK_RESPONSE) {
1016 initiator_sockets_.at(socket_id)->b_transport(trans, t);
1017 }
1018}
1019
1020template <unsigned int BUSWIDTH, typename TYPES>
1021unsigned int PcieMappingInterconnect<BUSWIDTH, TYPES>::
1022transaction_transport_dbg(tlm::tlm_generic_payload &trans) { // NOLINT
1023 trans.set_response_status(tlm::TLM_OK_RESPONSE);
1024 size_t socket_id = validateTransaction(trans);
1025 if (trans.get_response_status() == tlm::TLM_OK_RESPONSE) {
1026 return initiator_sockets_.at(socket_id)->transport_dbg(trans);
1027 } else {
1028 return 0;
1029 }
1030}
1031
1032/*
1033 * For pcie-map, just relay the incoming BW transaction to target socket
1034 */
1035template <unsigned int BUSWIDTH, typename TYPES>
1036void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1037pcie_map_invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
1038 sc_dt::uint64 end_range) {
1039 pcie_map_target_socket_->invalidate_direct_mem_ptr(start_range,
1040 end_range);
1041}
1042
1043/*
1044 * For pcie-map, just relay the incoming transaction to outgoing socket
1045 */
1046template <unsigned int BUSWIDTH, typename TYPES>
1047void PcieMappingInterconnect<BUSWIDTH, TYPES>::pcie_map_b_transport(
1048 tlm::tlm_generic_payload &trans, // NOLINT
1049 sc_core::sc_time &t) { // NOLINT
1050 pcie_map_initiator_socket->b_transport(trans, t);
1051}
1052
1053/*
1054 * For pcie-map, just relay the incoming transaction to outgoing socket
1055 */
1056template <unsigned int BUSWIDTH, typename TYPES>
1057unsigned int PcieMappingInterconnect<BUSWIDTH, TYPES>::pcie_map_transport_dbg(
1058 tlm::tlm_generic_payload &trans) { // NOLINT
1059 return pcie_map_initiator_socket->transport_dbg(trans);
1060}
1061
1062/*
1063 * For pcie-map, just relay the incoming transaction to outgoing socket
1064 */
1065template <unsigned int BUSWIDTH, typename TYPES>
1066bool PcieMappingInterconnect<BUSWIDTH, TYPES>::pcie_map_get_direct_mem_ptr(
1067 tlm::tlm_generic_payload &trans, // NOLINT
1068 tlm::tlm_dmi &dmi_data) { // NOLINT
1069 return pcie_map_initiator_socket->get_direct_mem_ptr(trans, dmi_data);
1070}
1071
1072template <unsigned int BUSWIDTH, typename TYPES>
1073void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1074castToTarget(sc_core::sc_object *object, supported_target_socket_t *target) {
1075 if (object == nullptr) {
1076 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
1077 "Unable to castToTarget from a nullptr");
1078 return;
1079 }
1080
1081 auto *tlm_target = dynamic_cast<
1082 tlm::tlm_target_socket<BUSWIDTH, TYPES> *>(object);
1083 if (tlm_target) {
1084 target->tlm = tlm_target;
1085 return;
1086 }
1087
1088 auto *multi_target = dynamic_cast<
1089 tlm_utils::multi_target_base<BUSWIDTH, TYPES> *>(object);
1090 if (multi_target) {
1091 target->multi = multi_target;
1092 return;
1093 }
1094
1095 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
1096 "Unable to dynamic-cast PCIe target socket");
1097}
1098
1099} // namespace composite
1100} // namespace systemc
1101} // namespace simics
1102
1103#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:867
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:528
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:462
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:534
Generic extension sender initialized with a TLM2 initiator socket of TSocket type.
Definition: extension_sender.h:35
virtual void send_failed(Transaction *transaction)
Called by extension if method_call invocation was missing.
Definition: extension_sender.h:58
void init(TSocket *socket)
Definition: extension_sender.h:40
virtual Transaction transaction()
Called by extension to get a new Transaction.
Definition: extension_sender.h:49
virtual void send_extension(Transaction *transaction)
Called by extension after the extension is set on the payload.
Definition: extension_sender.h:55
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:80
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