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 <algorithm>
33#include <cstring>
34#include <sstream>
35#include <map>
36#include <vector>
37#include <stdexcept>
38#include <string>
39#include <utility> // pair, ignore
40
41namespace { // NOLINT(build/namespaces_headers)
42const char *const PCIE_MAPPING_INTERCONNECT_TAG = \
43 "intel/PcieMappingInterconnect";
44
46const char *pcieTypeName(simics::types::pcie_type_t t) {
47 switch (t) {
49 return "Not Set";
51 return "Memory";
53 return "I/O";
55 return "Config";
57 return "Message";
59 return "Other";
60 default:
61 return "Unknown";
62 }
63}
64
65std::string pcieDeviceIdStr(uint16_t device_id) {
66 std::ostringstream os;
67 os << "device id 0x" << std::hex << device_id
68 << " (" << std::dec << (device_id >> 8)
69 << ":" << ((device_id >> 3) & 0x1f)
70 << "." << (device_id & 7) << ")";
71 return os.str();
72}
73} // namespace
74
75namespace simics {
76namespace systemc {
77namespace composite {
78
81template<typename TSocket>
83 : public iface::ExtensionSender<TSocket> {
84 public:
86
87 // iface::ExtensionSender
91 }
95 }
96
98};
99
106template <unsigned int BUSWIDTH, typename TYPES>
107class PcieMappingInterconnect : public sc_core::sc_module {
108 // Socket types used by the interconnect
109 typedef tlm_utils::simple_target_socket<
110 PcieMappingInterconnect, BUSWIDTH, TYPES> target_socket_t;
111 typedef tlm_utils::simple_initiator_socket<
112 PcieMappingInterconnect, BUSWIDTH, TYPES> initiator_socket_t;
113 typedef struct {
114 tlm::tlm_target_socket<BUSWIDTH, TYPES> *tlm;
115 tlm_utils::multi_target_base<BUSWIDTH, TYPES> *multi;
116 } supported_target_socket_t;
117
118 public:
119#ifndef SYSTEMC_3_0_0
121#endif
123 sc_core::sc_module_name = "PcieMappingInterconnect")
124 : transaction_target_socket("transaction_target_socket"),
125 pcie_device_target_socket("dummy_pcie_device_target_socket"),
126 pcie_map_initiator_socket("pcie_map_initiator_socket"),
127 initiator_sockets_("initiator_socket"),
128 config_target_socket_(),
129 msg_target_socket_(),
130 pcie_map_target_socket_("pcie_map_target_socket") {
131 transaction_target_socket.register_b_transport(
132 this, &PcieMappingInterconnect::transaction_b_transport);
133 transaction_target_socket.register_transport_dbg(
134 this, &PcieMappingInterconnect::transaction_transport_dbg);
135 pcie_map_initiator_socket.register_invalidate_direct_mem_ptr(
136 this, &PcieMappingInterconnect::pcie_map_invalidate_direct_mem_ptr);
137 pcie_map_target_socket_.register_b_transport(
138 this, &PcieMappingInterconnect::pcie_map_b_transport);
139 pcie_map_target_socket_.register_transport_dbg(
140 this, &PcieMappingInterconnect::pcie_map_transport_dbg);
141 pcie_map_target_socket_.register_get_direct_mem_ptr(
142 this, &PcieMappingInterconnect::pcie_map_get_direct_mem_ptr);
143
145 pcie_map_extension_.init(&sender_);
146
147 // f0 is required
148 function_data_[0];
149
150 SC_METHOD(warmReset);
151 sensitive << warm_reset_pin.pos();
152 dont_initialize();
153
154 config_target_socket_.tlm = nullptr;
155 config_target_socket_.multi = nullptr;
156 msg_target_socket_.tlm = nullptr;
157 msg_target_socket_.multi = nullptr;
158 }
159
160 // sc_core::sc_module
162
163 // Called by (outer) composite class from PcieDevice::connected
164 void connected(uint16_t device_id);
165
166 // Called by (outer) composite class from PcieDevice::disconnected
167 void disconnected(uint16_t device_id);
168
169 // Called by (outer) composite class from PcieDevice::hot_reset
170 void hotReset();
171
184 ConfObjectRef o,
185 iface::PcieSriovQueryInterface *sriov = nullptr);
186
187 // Create map helpers which are mapped on the cfg space. It contains
188 // PCI information like PCIe type and function id which is used to
189 // route the transaction to the right TLM2 socket
191
192 // Return the current map from address range to socket ID (Memory)
193 std::map<std::pair<size_t, size_t>, size_t> addressIdMemMap() const;
194
195 // Return the current map from address range to socket ID (IO)
196 std::map<std::pair<size_t, size_t>, size_t> addressIdIoMap() const;
197
198 // Sockets bound by external logic (see also private scope):
199 // Simics -> IC, intercepts the incoming PCIe transactions
201 // Dummy socket as PcieDevice is handled by connected/disconnected/hotReset
204 initiator_socket_t pcie_map_initiator_socket;
205 // Simics -> IC, receives warm reset signal
206 sc_core::sc_in<bool> warm_reset_pin;
207 // For MEM/IO, this parameter controls if the base address of the
208 // memory range is subtracted before sending the GP payload to endpoint
210
211 private:
212 // Called when warm_reset_pin is positive
213 void warmReset();
214
215 // Help methods
216 // Adds the BAR mappings of type with map info
217 void addMap(types::map_info_t info, types::pcie_type_t type);
218
219 // Removes the BAR mappings of type for socket ID
220 void delMap(size_t socket_id, types::pcie_type_t type);
221
222 // The supplied device_id passed to addFunction & delFunction should
223 // contain the full function ID and not just the function number.
224 // Adds the function's configuration header in the upstream target
225 void addFunction(uint16_t device_id);
226 // Removes the current mapping (if any) of this function's configuration
227 // header in the upstream target
228 void delFunction(uint16_t device_id);
229
230 // Cast sc_object to a supported target socket
231 void castToTarget(sc_core::sc_object *object,
232 supported_target_socket_t *target);
233
234 // Update the PCIe mapping
235 // @param function_id the config id used to index into function_data_,
236 // -1 means checks all config ids
237 // @param bar_id the bar id used to index into base_address,
238 // -1 means checks all bar ids
239 void updateMappings(int function_id = -1, int bar_id = -1);
240
241 /*
242 * When command register is written to, the mappings need to be re-evaluated
243 * and possibly updated.
244 *
245 * There is no error handling, so if the mapping fails this will only be
246 * logged but the access will still pass (i.e. transaction forwarded to
247 * target device where registers are updated without side-effects)
248 */
249 void updateCommand(uint16_t function_id, uint16_t old_command);
250
251 // Get up-to-date BAR/CMD information whenever we need it. It cannot be
252 // cached in the interconnect module as it might change over time by the
253 // model, reverse execution, checkpoint restore, ...
254 // @param id is the accessing initiator socket ID
255 // @param bar_id is the BAR number currently access, or -1 for config
256 // register access in which case all BARs must be retrieved
257 void retrieveFunctionData(size_t socket_id, int bar_id);
258
259 // Intercept Config space header write access and update the mapping table
260 void interceptCfgHeaderAccess(size_t socket_id,
261 tlm::tlm_generic_payload &trans); // NOLINT
262
263 // Find the id of the initiator_sockets_ list to route the transaction to
264 // based on the PCIe transaction type, function number and offset
265 size_t findSocketId(types::pcie_type_t type, uint16_t function_id,
266 tlm::tlm_generic_payload &trans) const; // NOLINT
267
268 // Return the created cfg map helper by device_id
269 conf_object_t *getCfgMapHelper(uint16_t device_id);
270
271 // Validate and update TLM2 response status
272 // If it passes validation it returns the socket-id as well
273 size_t validateTransaction(tlm::tlm_generic_payload &trans); // NOLINT
274 /*
275 * The single entry for all incoming PCIe transactions from Simics
276 * Depends on the PCIe type (CFG/MEM/IO/MSG), the transaction is
277 * routed to the corresponding initiator socket towards the endpoint device
278 * For CFG space header write, updating the mapping table as well
279 */
280 void transaction_b_transport(tlm::tlm_generic_payload &trans, // NOLINT
281 sc_core::sc_time &t); // NOLINT
282 unsigned int transaction_transport_dbg(
283 tlm::tlm_generic_payload &trans); // NOLINT
284
285 void pcie_map_invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
286 sc_dt::uint64 end_range);
287 void pcie_map_b_transport(
288 tlm::tlm_generic_payload &trans, // NOLINT: SystemC API
289 sc_core::sc_time &t); // NOLINT: SystemC API
290 unsigned int pcie_map_transport_dbg(
291 tlm::tlm_generic_payload &trans); // NOLINT: SystemC API
292 bool pcie_map_get_direct_mem_ptr(
293 tlm::tlm_generic_payload &trans, // NOLINT: SystemC API
294 tlm::tlm_dmi &dmi_data); // NOLINT: SystemC API
295
296 // Sockets created and bound by internal logic (see also public scope):
297 // IC -> Device, forwards the incoming PCIe transactions
298 sc_core::sc_vector<initiator_socket_t> initiator_sockets_;
299 supported_target_socket_t config_target_socket_;
300 supported_target_socket_t msg_target_socket_;
301 // Device -> IC, intercept upstream pcie-map transactions
302 target_socket_t pcie_map_target_socket_;
303
304 struct cfg_header_t {
305 uint16_t command; // command register
306 uint32_t base_address[7]; // 6 BARs + expansion ROM BAR
307 };
308 // Cache each function's cfg header, f0 is required
309 std::map<uint16_t, cfg_header_t> function_data_;
310 std::vector<iface::PcieBaseAddressRegisterQueryInterface::PCIeBar>
311 bar_info_;
312
313 // Extension and sender for talking to the Simics pcie upstream target
314 PcieMappingInterconnectExtensionSender<initiator_socket_t> sender_;
315 iface::PcieMapExtension pcie_map_extension_;
316
317 // Trigger hot reset on the device
318 iface::PcieResetInterface *reset_ {nullptr};
319
320 // A map to store MEM address ranges and corresponding socket IDs
321 std::map<std::pair<size_t, size_t>, size_t> address_id_mem_map_;
322 // A map to store IO address ranges and corresponding socket IDs
323 std::map<std::pair<size_t, size_t>, size_t> address_id_io_map_;
324 // A map from function_number/bar_offset to socket IDs
325 std::map<std::pair<size_t, size_t>, size_t> fn_bar_id_map_;
326
327 ConfObjectRef simulation_obj_ {nullptr};
328 // For PCIe gasket object, the pcie_device and transaction interfaces
329 // are implemented on the gasket_obj_. For other cases, it is the same
330 // object as simulation_obj_
331 ConfObjectRef gasket_obj_ {nullptr};
332
333 // In some case (for example ARI), function number has 8 bits
334 // This boolean variable is set when the function number from getBarInfo()
335 // returns a value greater than 7
336 bool function_number_has_8bits_ {false};
337
338 // Optional static SR-IOV configuration provided by the device.
339 // When set, connected() uses it instead of transport_dbg.
340 iface::PcieSriovQueryInterface *sriov_query_ {nullptr};
341
342 // SR-IOV support ---------------------------------------------------------
343 // Populated by connected() from sriov_query_ (if set).
344 // One entry per PF that exposes VFs (multi-PF devices have multiple
345 // entries).
346 struct SriovState {
347 uint16_t cap_offset = 0; // Extended Cap byte offset in PF cfg
348 int pf_function = 0; // PF function number (SR-IOV cap owner)
349 int total_vfs = 0; // TotalVFs: hardware maximum (RO)
350 int first_vf_function = 0; // Function number of VF0
351 int vf_stride = 1; // VF Stride: step between VF functions
352
353 // Cached VF BAR base addresses, written by the OS into the SR-IOV
354 // Extended Capability BAR registers (+0x24..+0x38, 6 × 4-byte slots).
355 // Index i = (cap_rel_offset - 0x24) / 4, which equals
356 // (vf_cfg_bar_offset - 0x10) / 4 — the same index used by
357 // cfg_header_t::base_address[] for the VF config-header BAR:
358 // vf_bar[0] ↔ cap+0x24 ↔ VF cfg BAR offset 0x10
359 // vf_bar[1] ↔ cap+0x28 ↔ VF cfg BAR offset 0x14
360 // vf_bar[2] ↔ cap+0x2C ↔ VF cfg BAR offset 0x18
361 // vf_bar[3] ↔ cap+0x30 ↔ VF cfg BAR offset 0x1C
362 // vf_bar[4] ↔ cap+0x34 ↔ VF cfg BAR offset 0x20
363 // vf_bar[5] ↔ cap+0x38 ↔ VF cfg BAR offset 0x24
364 // The SR-IOV spec §9.3.3.14 guarantees every VF shares
365 // the same BAR layout.
366 uint32_t vf_bar[6] = {};
367
368 // Cached register values updated by interceptCfgHeaderAccess()
369 uint16_t control = 0;
370 uint16_t num_vfs = 0; // NumVFs: OS-programmed active VF count
371 };
372 std::vector<SriovState> sriov_states_;
373
374 // Populate sriov_states_ from sriov_query_. Called from connect() so
375 // that SR-IOV config-space snooping works even when
376 // pcie_device.connected() is never invoked (e.g. direct upstream_target
377 // assignment in tests). Also called from connected() to reset runtime
378 // state on reconnect.
379 void setupSriovStates();
380
381 // Map/unmap VF config space in the Simics pcie_map
382 // (enable_function / disable_function).
383 // Triggered when VF Enable (SR-IOV Control bit 0) transitions.
384 void updateVfCfgMappings(SriovState &state, bool enable);
385
386 // Map/unmap VF BAR memory regions (add_map / del_map).
387 // Triggered when VF MSE (SR-IOV Control bit 3) transitions while
388 // VF Enable is set, or when VF Enable transitions while VF MSE is set.
389 void updateVfBarMappings(SriovState &state, bool enable);
390};
391
392template <unsigned int BUSWIDTH, typename TYPES>
394 // Because of the way SystemC has been designed, we cannot create new
395 // sockets dynamically ouside of the CTOR except for in the
396 // before_end_of_elaboration() method.
397
398 // Create initiator sockets equal to the number of Config, Mem/IO
399 // target sockets and Msg target socket
400 initiator_sockets_.init(function_data_.size() + bar_info_.size() + 1);
401
402 size_t socket_id = 0;
403 // Allocate and initialize function data based on device configuration
404 for (; socket_id < function_data_.size(); ++socket_id) {
405 if (config_target_socket_.tlm) {
406 initiator_sockets_[socket_id].bind(*config_target_socket_.tlm);
407 } else {
408 initiator_sockets_[socket_id].bind(*config_target_socket_.multi);
409 }
410 }
411
412 for (const auto &bar : bar_info_) {
413 supported_target_socket_t target = {};
414 castToTarget(bar.target_socket, &target);
415 if (target.tlm) {
416 initiator_sockets_[socket_id].bind(*target.tlm);
417 } else if (target.multi) {
418 initiator_sockets_[socket_id].bind(*target.multi);
419 } else {
420 SC_REPORT_ERROR(
421 PCIE_MAPPING_INTERCONNECT_TAG,
422 "BAR target_socket type is not supported");
423 }
424 fn_bar_id_map_[std::make_pair(bar.function, bar.offset)] = socket_id;
425 ++socket_id;
426 }
427
428 if (msg_target_socket_.tlm) {
429 initiator_sockets_[socket_id].bind(*msg_target_socket_.tlm);
430 } else {
431 initiator_sockets_[socket_id].bind(*msg_target_socket_.multi);
432 }
433}
434
435template <unsigned int BUSWIDTH, typename TYPES>
437 // Use the side effect of it to set device object on pcie map gasket.
438 // The device object is used internally for get the mapping helper port
439 // object
440 pcie_map_extension_.get_device_id(gasket_obj_);
441
442 // The PCIe Modeling Library requires endpoints to add their functions
443 // and map resources like Memory and I/O BARs. However, it is unlikely
444 // that these resources are configured before the connection. Therefore,
445 // we deviate from the PCIe DML Modeling Library by not performing the
446 // mapping at this stage.
447 for (const auto &fd : function_data_) {
448 delFunction(device_id | fd.first);
449 addFunction(device_id | fd.first);
450 }
451
452 // Populate SR-IOV state from the static PcieSriovQueryInterface.
453 setupSriovStates();
454
455 // PCIe SR-IOV spec §9.3.3.3: VF config space must remain hidden
456 // until VF Enable (SR-IOV Control bit 0) is set. All VF functions
457 // were registered above via add_function(); immediately suppress them
458 // with disable_function() so they start in the correct hidden state.
459 for (const auto &state : sriov_states_) {
460 for (int i = 0; i < state.total_vfs; ++i) {
461 const uint16_t fn = static_cast<uint16_t>(
462 state.first_vf_function
463 + i * state.vf_stride);
464 pcie_map_extension_.disable_function(fn);
465 }
466 }
467}
468
469template <unsigned int BUSWIDTH, typename TYPES>
471disconnected(uint16_t device_id) {
472 for (auto &fd : function_data_) {
473 delFunction(device_id | fd.first);
474 // disabled both MEM and IO
475 fd.second.command = 0;
476 updateMappings(fd.first);
477 }
478}
479
480template <unsigned int BUSWIDTH, typename TYPES>
482 if (reset_) {
483 reset_->hotReset();
484 for (auto &fd : function_data_) {
485 // disabled both MEM and IO
486 fd.second.command = 0;
487 updateMappings(fd.first);
488 }
489 // Undo VF config space routing (governed by VF Enable, bit 0).
490 // BAR map cleanup was already performed by the updateMappings() loop
491 // above (fd.command → 0 for all VF functions including active VFs).
492 // Guard disable_function on VF Enable being set: if VFs were never
493 // enabled (or the device was already disconnected), pcie_map_extension_
494 // has no upstream provider and calling it would abort.
495 for (auto &state : sriov_states_) {
496 if (state.control & 0x1u) { // VF Enable was set
497 updateVfCfgMappings(state, false);
498 }
499 state.control = 0;
500 state.num_vfs = 0;
501 std::fill(std::begin(state.vf_bar), std::end(state.vf_bar), 0u);
502 }
503 }
504}
505
506template <unsigned int BUSWIDTH, typename TYPES>
508 if (reset_) {
509 reset_->warmReset();
510 for (auto &fd : function_data_) {
511 // disabled both MEM and IO
512 fd.second.command = 0;
513 updateMappings(fd.first);
514 }
515 }
516}
517
518template <unsigned int BUSWIDTH, typename TYPES>
519void PcieMappingInterconnect<BUSWIDTH, TYPES>::
520addFunction(uint16_t device_id) {
521 std::ostringstream os;
522 os << "Adding function for device_id: "
523 << device_id;
524 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
525 auto *map_helper = getCfgMapHelper(device_id);
526 if (map_helper == nullptr) {
527 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
528 "Failure to get map helper");
529 return;
530 }
531 pcie_map_extension_.add_function(map_helper, device_id);
532 if (sender_.failed_transaction_.payload()) {
533 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
534 "Failure to add function");
535 return;
536 }
537}
538
539template <unsigned int BUSWIDTH, typename TYPES>
540void PcieMappingInterconnect<BUSWIDTH, TYPES>::
541delFunction(uint16_t device_id) {
542 std::ostringstream os;
543 os << "Deleting function for device_id: "
544 << device_id;
545 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
546 auto *map_helper = getCfgMapHelper(device_id);
547 if (map_helper == nullptr) {
548 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
549 "Failure to get map helper");
550 return;
551 }
552 pcie_map_extension_.del_function(map_helper, device_id);
553 if (sender_.failed_transaction_.payload()) {
554 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
555 "Failure to delete function");
556 return;
557 }
558}
559
560template <unsigned int BUSWIDTH, typename TYPES>
564 iface::PcieResetInterface *reset, ConfObjectRef o,
566 if (pci == nullptr) {
567 SIM_LOG_ERROR(
569 "PcieDeviceQueryInterface not implemented on the device");
570 return;
571 }
572
573 if (bar == nullptr) {
574 SIM_LOG_ERROR(
576 "PcieBaseAddressRegisterQueryInterface not implemented on"
577 " the device");
578 return;
579 }
580
581 if (reset == nullptr) {
582 SIM_LOG_ERROR(o, Log_Configuration,
583 "PcieResetInterface not implemented on the device");
584 return;
585 }
586
587 // Get the config target_socket from the device
588 castToTarget(pci->getConfigTargetSocket(), &config_target_socket_);
589
590 // Get the message target_socket from the device
591 castToTarget(pci->getMsgTargetSocket(), &msg_target_socket_);
592
593 // Get the BAR info
594 bar_info_ = bar->getBarInfo();
595
596 // Set the reset interface
597 reset_ = reset;
598
599 // Calculate how many functions are there (f0 is required)
600 for (const auto &bar : bar_info_) {
601 if (function_data_.find(bar.function) == function_data_.end()) {
602 function_data_[bar.function];
603 if (bar.function > 7) {
604 function_number_has_8bits_ = true;
605 }
606 }
607 }
608
609 // Bind pcie_map_target_socket_ so device can call pcie_map interface
610 auto *initiator = dynamic_cast<
611 tlm::tlm_initiator_socket<BUSWIDTH, TYPES> *>(
613 if (initiator) {
614 pcie_map_target_socket_.bind(*initiator);
615 } else {
616 SIM_LOG_ERROR(o, Log_Configuration,
617 "object returned by getPcieMapInitiatorSocket was not"
618 " a tlm::tlm_initiator_socket.");
619 }
620
621 simulation_obj_ = o;
622 sriov_query_ = sriov;
623 setupSriovStates();
624 createCfgMapHelper();
625}
626
627template <unsigned int BUSWIDTH, typename TYPES>
628std::map<std::pair<size_t, size_t>, size_t>
630 return address_id_mem_map_;
631}
632
633template <unsigned int BUSWIDTH, typename TYPES>
634std::map<std::pair<size_t, size_t>, size_t>
636 return address_id_io_map_;
637}
638
639template <unsigned int BUSWIDTH, typename TYPES>
642 sriov_states_.clear();
643 if (sriov_query_ != nullptr) {
644 for (const auto &info : sriov_query_->getSriovInfo()) {
645 SriovState state = {};
646 state.cap_offset = info.cap_offset;
647 state.pf_function = info.pf_function;
648 state.total_vfs = info.total_vfs;
649 state.first_vf_function = info.first_vf_function;
650 state.vf_stride = (info.vf_stride <= 0) ? 1 : info.vf_stride;
651
652 // SR-IOV spec §9.3.3.10: VF BARs must be Memory Space only.
653 for (const auto &bi : bar_info_) {
654 const int fn = bi.function;
655 const bool is_vf =
656 (fn >= state.first_vf_function)
657 && ((fn - state.first_vf_function) % state.vf_stride == 0)
658 && ((fn - state.first_vf_function) / state.vf_stride
659 < state.total_vfs);
660 if (is_vf && !bi.is_memory) {
661 std::ostringstream oss;
662 oss << "SR-IOV: VF BAR (function=" << fn
663 << " offset=0x" << std::hex << bi.offset
664 << ") has is_memory=false; VF BARs must be"
665 " Memory Space only per PCIe SR-IOV spec";
666 SIM_LOG_ERROR(simulation_obj_, Log_Configuration,
667 "%s", oss.str().c_str());
668 }
669 }
670
671 sriov_states_.push_back(state);
672 }
673 }
674}
675
676// Map or unmap VF config space entries in the Simics pcie_map.
677// Called when VF Enable (SR-IOV Control bit 0) transitions.
678template <unsigned int BUSWIDTH, typename TYPES>
679void PcieMappingInterconnect<BUSWIDTH, TYPES>::
680updateVfCfgMappings(SriovState &state, bool enable) {
681 const int num_vfs = static_cast<int>(state.num_vfs);
682 {
683 std::ostringstream os;
684 os << "SR-IOV PF" << state.pf_function << ": "
685 << (enable ? "enabling " : "disabling ") << num_vfs
686 << " VF config space(s)";
687 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
688 }
689 for (int i = 0; i < num_vfs; ++i) {
690 const uint16_t fn = static_cast<uint16_t>(
691 state.first_vf_function + i * state.vf_stride);
692 if (enable) {
693 pcie_map_extension_.enable_function(fn);
694 } else {
695 pcie_map_extension_.disable_function(fn);
696 }
697 }
698}
699
700// Map or unmap VF BAR memory regions in the Simics pcie_map.
701// Called when VF MSE (SR-IOV Control bit 3) transitions while VF Enable is
702// set, or when VF Enable transitions while VF MSE is already set.
703template <unsigned int BUSWIDTH, typename TYPES>
704void PcieMappingInterconnect<BUSWIDTH, TYPES>::
705updateVfBarMappings(SriovState &state, bool enable) {
706 const int num_vfs = static_cast<int>(state.num_vfs);
707
708 // NumVFs is OS-programmed at runtime; validate it does not exceed the
709 // hardware maximum TotalVFs declared by PcieSriovQueryInterface.
710 if (num_vfs > state.total_vfs) {
711 std::ostringstream os;
712 os << "SR-IOV: NumVFs (" << num_vfs
713 << ") exceeds TotalVFs (" << state.total_vfs
714 << ") for PF" << state.pf_function
715 << " — refusing to update VF BAR mappings";
716 SC_REPORT_WARNING(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
717 return;
718 }
719
720 // Iterate over VF0's BAR entries to discover the layout (size_bits,
721 // is_64bit etc.). The SR-IOV spec §9.3.3.14 guarantees every VF shares
722 // the same BAR layout. vf_bar[i] == (cap+0x24 + i*4) == cfg BAR offset
723 // (0x10+i*4), so the index maps 1:1 to cfg_header_t::base_address[].
724 for (const auto &bi : bar_info_) {
725 if (bi.function != state.first_vf_function || !bi.is_memory)
726 continue;
727
728 const int ba_idx = (bi.offset - 0x10) / 4;
729 if (ba_idx < 0 || ba_idx >= 6)
730 continue;
731
732 const int sz = bi.size_bits;
733 if (sz >= 64) {
734 // Silently skip bad configurations. This should be validated by
735 // the initial query and not here on each map/unmap call.
736 continue;
737 }
738 const uint32_t lo = state.vf_bar[ba_idx];
739 const uint32_t hi = (bi.is_64bit && ba_idx + 1 < 6)
740 ? state.vf_bar[ba_idx + 1] : 0u;
741 const uint64_t vf_bar_raw = (static_cast<uint64_t>(hi) << 32) | lo;
742 const uint64_t align_mask = ~((1ULL << sz) - 1ULL);
743 const uint64_t vf_base = vf_bar_raw & align_mask;
744
745 {
746 std::ostringstream os;
747 os << "SR-IOV PF" << state.pf_function
748 << " VF BAR (cfg 0x" << std::hex << bi.offset << "): "
749 << (enable ? "enabling " : "disabling ")
750 << std::dec << num_vfs << " VF BAR(s)";
751 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
752 }
753
754 // Loop only over the NumVFs VFs that are active (or were active).
755 for (int i = 0; i < num_vfs; ++i) {
756 const uint16_t fn = static_cast<uint16_t>(
757 state.first_vf_function + i * state.vf_stride);
758 auto it = function_data_.find(fn);
759 if (it == function_data_.end()) {
760 std::ostringstream os;
761 os << "SR-IOV PF" << state.pf_function
762 << ": VF function " << fn
763 << " not found in function_data_ — inconsistent SR-IOV"
764 " configuration (getSriovInfo() and getBarInfo()"
765 " disagree)";
766 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
767 os.str().c_str());
768 return;
769 }
770 auto &fd = it->second;
771 // VF Command register has MSE hardwired to 0 (PCIe spec
772 // §7.5.1.1.3) — do not use fd.command to gate VF BAR
773 // mappings. Drive add_map / del_map directly so that the
774 // VF's command register is never forged.
775 const size_t socket_id = fn_bar_id_map_.at(
776 std::make_pair(static_cast<size_t>(fn),
777 static_cast<size_t>(bi.offset)));
778 delMap(socket_id, types::PCIE_Type_Mem);
779 if (enable) {
780 const uint64_t vf_addr =
781 vf_base + static_cast<uint64_t>(i) * (1ULL << sz);
782 fd.base_address[ba_idx] = static_cast<uint32_t>(vf_addr);
783 if (bi.is_64bit && ba_idx + 1 < 7)
784 fd.base_address[ba_idx + 1] =
785 static_cast<uint32_t>(vf_addr >> 32);
786 const types::map_info_t info(
787 vf_addr, vf_addr, 1ULL << sz,
788 static_cast<int>(fn));
789 address_id_mem_map_[std::make_pair(
790 vf_addr, vf_addr + (1ULL << sz))] = socket_id;
791 addMap(info, types::PCIE_Type_Mem);
792 } else {
793 fd.base_address[ba_idx] = 0;
794 if (bi.is_64bit && ba_idx + 1 < 7)
795 fd.base_address[ba_idx + 1] = 0;
796 }
797 }
798 }
799}
800
801template <unsigned int BUSWIDTH, typename TYPES>
802void PcieMappingInterconnect<BUSWIDTH, TYPES>::
803retrieveFunctionData(size_t socket_id, int bar_id) {
804 if (initiator_sockets_.size() <= socket_id) {
805 std::ostringstream os;
806 os << "Invalid socket_id: " << socket_id;
807 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
808 return;
809 }
810
811 tlm::tlm_generic_payload trans;
812 // The data pointer, length and stream width attribute are reused
813 // since they are not modifiable by the target according to the spec
814 uint32_t value = 0;
815 trans.set_data_ptr(reinterpret_cast<unsigned char *>(&value));
816 trans.set_data_length(4);
817 trans.set_streaming_width(4);
818
819 trans.set_command(tlm::TLM_READ_COMMAND);
820 trans.set_address(0x4);
821
822 auto ret = initiator_sockets_[socket_id]->transport_dbg(trans);
823 if (ret == 0) {
824 // The target is not able to perform the transport_dbg.
825 // The PCIe interconnector is unable to get the up-to-date
826 // BAR/CMD information which may cause unexpected behavior
827 return;
828 }
829
830 auto fd = function_data_.begin();
831 std::advance(fd, socket_id);
832 fd->second.command = value;
833
834 // NOTE: the IO, Mem32, Mem64 bits are ignored by updateMapping,
835 // no need to filter them out here
836 for (const auto &bar : bar_info_) {
837 // BarInfo holds _all_ BARs for _all_ functions. This can probably
838 // be done in a much more clever way that avoids iterating over the
839 // entire BarInfo for each function in the outer loop.
840 if (bar.function != fd->first) {
841 continue;
842 }
843
844 int expected_bar_id = bar.offset == 0x30 ? 6 : (bar.offset - 0x10) / 4;
845 // bar_id == -1 => read all bars
846 if (bar_id != -1 && bar_id != expected_bar_id) {
847 continue;
848 }
849
850 trans.set_address(bar.offset);
851 initiator_sockets_[socket_id]->transport_dbg(trans);
852 fd->second.base_address[expected_bar_id] = value;
853 if (bar.is_64bit) {
854 if (expected_bar_id == 6) {
855 SC_REPORT_ERROR(
856 PCIE_MAPPING_INTERCONNECT_TAG,
857 "Expansion ROM BAR can't support 64-bit address");
858 } else {
859 // Read higher 32 bits in next BAR
860 trans.set_address(bar.offset + 4);
861 initiator_sockets_[socket_id]->transport_dbg(trans);
862 fd->second.base_address[expected_bar_id + 1] = value;
863 }
864 }
865 }
866}
867
868template <unsigned int BUSWIDTH, typename TYPES>
869void PcieMappingInterconnect<BUSWIDTH, TYPES>::
870addMap(types::map_info_t info, types::pcie_type_t type) {
871 std::ostringstream os;
872 os << "Adding mapping of type: "
873 << pcieTypeName(type);
874 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
875 pcie_map_extension_.add_map(info, type);
876 if (sender_.failed_transaction_.payload()) {
877 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, "Failure to add map");
878 return;
879 }
880}
881
882template <unsigned int BUSWIDTH, typename TYPES>
883void PcieMappingInterconnect<BUSWIDTH, TYPES>::
884delMap(size_t socket_id, types::pcie_type_t type) {
885 types::map_info_t::physical_address_t base = 0xffffffffffffffff;
886 if (type == types::PCIE_Type_Mem) {
887 auto it = std::find_if(
888 address_id_mem_map_.begin(),
889 address_id_mem_map_.end(),
890 [socket_id](
891 const std::pair<std::pair<size_t, size_t>, size_t> &entry) {
892 return entry.second == socket_id;
893 });
894
895 if (it != address_id_mem_map_.end()) {
896 base = it->first.first;
897 address_id_mem_map_.erase(it);
898 }
899 } else if (type == types::PCIE_Type_IO) {
900 auto it = std::find_if(
901 address_id_io_map_.begin(),
902 address_id_io_map_.end(),
903 [socket_id](
904 const std::pair<std::pair<size_t, size_t>, size_t> &entry) {
905 return entry.second == socket_id;
906 });
907
908 if (it != address_id_io_map_.end()) {
909 base = it->first.first;
910 address_id_io_map_.erase(it);
911 }
912 }
913
914 if (base != 0xffffffffffffffff) {
915 std::ostringstream os;
916 os << "Deleting mapping of type: "
917 << pcieTypeName(type) << ", base: 0x" << std::hex << base;
918 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
919
920 pcie_map_extension_.del_map(base, type);
921 if (sender_.failed_transaction_.payload()) {
922 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, "Failure to del map");
923 return;
924 }
925 }
926}
927
928template <unsigned int BUSWIDTH, typename TYPES>
929void PcieMappingInterconnect<BUSWIDTH, TYPES>::
930updateMappings(int function_id, int bar_id) {
931 for (const auto &bi : bar_info_) {
932 if (function_id != -1 && bi.function != function_id) {
933 continue;
934 }
935
936 auto header = function_data_.at(bi.function);
937
938 int expected_bar_id = bi.offset == 0x30 ? 6 : (bi.offset - 0x10) / 4;
939 if (bar_id != -1 && bar_id != expected_bar_id) {
940 continue;
941 }
942
943 uint64_t base_address = header.base_address[expected_bar_id];
944 if (bi.is_64bit) {
945 if (expected_bar_id == 6) {
946 SC_REPORT_ERROR(
947 PCIE_MAPPING_INTERCONNECT_TAG,
948 "Expansion ROM BAR can't support 64-bit address");
949 } else {
950 base_address |= static_cast<uint64_t>(
951 header.base_address[expected_bar_id + 1]) << 32;
952 }
953 }
954
955 bool enable = header.command & 1;
957 if (bi.is_memory) {
959 if (bi.offset == 0x30) {
960 enable = base_address & 1;
961 } else {
962 enable = (header.command >> 1) & 1;
963 }
964 }
965 base_address &= ~((1ULL << bi.size_bits) - 1);
966 size_t socket_id = fn_bar_id_map_.at(
967 std::make_pair(bi.function, bi.offset));
968 delMap(socket_id, type);
969 if (enable) {
970 // The `start` field in map_info_t is set to base,
971 // so later when receiving the transaction, the address
972 // can be used to route the transaction to the right socket
973 types::map_info_t info(base_address, base_address,
974 1ULL << bi.size_bits, function_id);
975 auto address_range = std::make_pair(info.base,
976 info.base + info.length);
977
978 if (bi.is_memory) {
979 address_id_mem_map_[address_range] = socket_id;
980 std::ostringstream os;
981 os << "Memory address for function " << bi.function
982 << " and bar ID " << expected_bar_id
983 << " in range of [0x" << std::hex << info.base
984 << "-0x" << info.base + info.length << "] maps to socket ID "
985 << socket_id;
986 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
987 } else {
988 address_id_io_map_[address_range] = socket_id;
989 std::ostringstream os;
990 os << "IO address for function " << bi.function
991 << " and bar ID " << expected_bar_id
992 << " in range of [0x" << std::hex << info.base
993 << "-0x" << info.base + info.length << "] maps to socket ID "
994 << socket_id;
995 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
996 }
997 addMap(info, type);
998 }
999 }
1000}
1001
1002template <unsigned int BUSWIDTH, typename TYPES>
1003void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1004updateCommand(uint16_t function_id, uint16_t old_command) {
1005 if (function_data_[function_id].command != old_command) {
1006 updateMappings(function_id);
1007 }
1008}
1009
1010template <unsigned int BUSWIDTH, typename TYPES>
1011void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1012interceptCfgHeaderAccess(size_t socket_id,
1013 tlm::tlm_generic_payload &trans) { // NOLINT
1014 unsigned int size = trans.get_data_length();
1015 // Check assumptions (but leave it up to target device to report errors)
1016 if (trans.get_byte_enable_ptr() || trans.get_streaming_width() != size) {
1017 return;
1018 }
1019
1020 // We are only interested in write access to BARn or Command regs
1021 if (!trans.is_write()) {
1022 return;
1023 }
1024
1025 // Maximum size is 8
1026 uint64_t *data = reinterpret_cast<uint64_t *>(trans.get_data_ptr());
1027
1028 auto it = function_data_.begin();
1029 std::advance(it, socket_id);
1030 uint16_t function_id = it->first;
1031 auto &header = it->second;
1032 sc_dt::uint64 offset = trans.get_address();
1033 switch (offset) {
1034 case 0x04: { // command & status
1035 uint16_t old_command = header.command;
1036 retrieveFunctionData(socket_id, -1);
1037 header.command = (*data) & 0xffff; // ignore status
1038 updateCommand(function_id, old_command);
1039 break;
1040 }
1041 case 0x10: // BAR0-BAR5
1042 case 0x14:
1043 case 0x18:
1044 case 0x1c:
1045 case 0x20:
1046 case 0x24: {
1047 int bar_id = (offset - 0x10) / 4;
1048 retrieveFunctionData(socket_id, bar_id);
1049 // NOTE: the IO, Mem32, Mem64 bits are ignored by
1050 // updateMapping, no need to filter them out here. In
1051 // addition, any bits less than the size of the BAR as defined
1052 // by the BarInfo will also be ignored/truncated by
1053 // updateMapping; making sure we always send a naturally
1054 // aligned address to Simics
1055 header.base_address[bar_id] = *data & 0xffffffff;
1056 if (size == 8 && !(offset % 8)) {
1057 header.base_address[bar_id + 1] = *data >> 32;
1058 }
1059 updateMappings(function_id, bar_id);
1060 break;
1061 }
1062 case 0x30: // Expansion ROM BAR
1063 int bar_id = 6;
1064 retrieveFunctionData(socket_id, bar_id);
1065 header.base_address[bar_id] = *data & 0xffffffff;
1066 updateMappings(function_id, bar_id);
1067 break;
1068 }
1069
1070 // SR-IOV Extended Capability snooping — iterate all PF SR-IOV groups.
1071 for (auto &state : sriov_states_) {
1072 if (static_cast<int>(function_id) != state.pf_function)
1073 continue;
1074 const sc_dt::uint64 cap = state.cap_offset;
1075 const uint32_t written = static_cast<uint32_t>(*data);
1076 if (offset == cap + 0x08) {
1077 const uint16_t old_ctrl = state.control;
1078 state.control = static_cast<uint16_t>(written & 0xFFFFu);
1079 // Per PCIe SR-IOV spec §9.3.3.3, the two bits have separate roles:
1080 // VF Enable (bit 0): VFs become enumerable; their config space
1081 // is accessible → enable_function / disable_function.
1082 // VF MSE (bit 3): VF memory space (BARs) responds to memory
1083 // transactions; requires VF Enable to be set first
1084 // → add_map / del_map.
1085 // The two transitions are handled independently so that software
1086 // can set VF Enable alone to probe VF config space before
1087 // committing BAR resources.
1088 const bool vf_enable_was = old_ctrl & 0x1u;
1089 const bool vf_enable_now = state.control & 0x1u;
1090 const bool vf_mse_was = (old_ctrl >> 3) & 0x1u;
1091 const bool vf_mse_now = (state.control >> 3) & 0x1u;
1092 const bool bars_were_live = vf_enable_was && vf_mse_was;
1093 const bool bars_are_live = vf_enable_now && vf_mse_now;
1094 if (vf_enable_was != vf_enable_now) {
1095 updateVfCfgMappings(state, vf_enable_now);
1096 }
1097 if (bars_were_live != bars_are_live) {
1098 updateVfBarMappings(state, bars_are_live);
1099 }
1100 } else if (offset == cap + 0x10) { // NumVFs (OS-programmed)
1101 state.num_vfs = static_cast<uint16_t>(written & 0xFFFFu);
1102 } else if (offset >= cap + 0x24 && offset < cap + 0x24 + 6 * 4) {
1103 // VF BAR registers +0x24..+0x38 (6 × 4-byte slots, §9.3.3.14).
1104 // Index = (cap_rel - 0x24)/4 == (vf_cfg_offset - 0x10)/4,
1105 // matching vf_bar[] and cfg_header_t::base_address[] directly.
1106 const int idx = static_cast<int>((offset - (cap + 0x24)) / 4);
1107 state.vf_bar[idx] = written;
1108 // §9.3.3.14: a VF BAR write changes the base address for ALL
1109 // VFs (BARb_VFv = VF_BARb + v×aperture). If BARs are live
1110 // (VF Enable && VF MSE), del_map old ranges and add_map new
1111 // ranges for every active VF.
1112 const bool vf_en = (state.control & 0x1u) != 0;
1113 const bool vf_mse = ((state.control >> 3) & 0x1u) != 0;
1114 if (vf_en && vf_mse)
1115 updateVfBarMappings(state, true);
1116 }
1117 break; // each function_id matches at most one PF group
1118 }
1119}
1120
1121template <unsigned int BUSWIDTH, typename TYPES>
1122size_t PcieMappingInterconnect<BUSWIDTH, TYPES>::
1123findSocketId(types::pcie_type_t type, uint16_t function_id,
1124 tlm::tlm_generic_payload &trans) const { // NOLINT
1125 if (type == types::PCIE_Type_Cfg) {
1126 auto found = function_data_.find(function_id);
1127 if (found == function_data_.end()) {
1128 std::ostringstream os;
1129 os << "Function id(" << function_id
1130 << ") not found for this device";
1131 throw std::invalid_argument {
1132 os.str()
1133 };
1134 }
1135 return std::distance(function_data_.begin(), found);
1136 } else if (type == types::PCIE_Type_Mem) {
1137 size_t address = trans.get_address();
1138 for (const auto &entry : address_id_mem_map_) {
1139 auto startAddress = entry.first.first;
1140 auto endAddress = entry.first.second;
1141 auto id = entry.second;
1142
1143 if (address >= startAddress && address < endAddress) {
1144 if (enable_base_address_subtraction) {
1145 trans.set_address(address - startAddress);
1146 }
1147 return id;
1148 }
1149 }
1150 throw std::invalid_argument {
1151 "Unable to find the initiator_socket to forward the MEM transaction"
1152 };
1153 } else if (type == types::PCIE_Type_IO) {
1154 size_t address = trans.get_address();
1155 for (const auto &entry : address_id_io_map_) {
1156 auto startAddress = entry.first.first;
1157 auto endAddress = entry.first.second;
1158 auto id = entry.second;
1159
1160 if (address >= startAddress && address < endAddress) {
1161 if (enable_base_address_subtraction) {
1162 trans.set_address(address - startAddress);
1163 }
1164 return id;
1165 }
1166 }
1167 throw std::invalid_argument {
1168 "Unable to find the initiator_socket to forward the IO transaction"
1169 };
1170 } else if (type == types::PCIE_Type_Msg) {
1171 return function_data_.size() + bar_info_.size();
1172 }
1173 throw std::invalid_argument {
1174 "Unsupported PCIe type"
1175 };
1176}
1177
1178template <unsigned int BUSWIDTH, typename TYPES>
1181 auto *map_helper = SIM_get_class("pcie_map_helper_cpp");
1182 if (map_helper == nullptr) {
1183 SIM_LOG_ERROR(
1184 simulation_obj_, Log_Configuration,
1185 "Simics class pcie_map_helper_cpp is required."
1186 "Try load the module pcie-map-helper-c++ first.");
1187 return;
1188 }
1189
1190 if (SIM_c_get_interface(simulation_obj_, "pcie_device") == nullptr) {
1191 // Make sure simulation_obj_ is ready to be used by other objects
1192 if (!SIM_object_is_configured(simulation_obj_)) {
1193 SIM_LOG_ERROR(
1194 simulation_obj_, Log_Configuration,
1195 "Simulation object is not configured yet, cannot get"
1196 " its gasket_list attribute");
1197 return;
1198 }
1199
1200 // For PCIe gasket object, the interface is not implemented
1201 // on the simulation object, but rather the gasket object
1202 auto gasket_list = SIM_get_attribute(simulation_obj_, "gasket_list");
1203 int gasket_count = SIM_attr_list_size(gasket_list);
1204 for (int i = 0; i < gasket_count; ++i) {
1205 auto obj = SIM_attr_object(SIM_attr_list_item(gasket_list, i));
1206 if (SIM_c_get_interface(obj, "pcie_device")) {
1207 gasket_obj_ = obj;
1208 break;
1209 }
1210 }
1211 if (gasket_obj_.object() == nullptr) {
1212 SIM_LOG_ERROR(
1213 simulation_obj_, Log_Configuration,
1214 "No gasket object implemented the required"
1215 " pcie_device interface");
1216 return;
1217 }
1218 } else {
1219 gasket_obj_ = simulation_obj_.object();
1220 }
1221
1222 for (const auto &fd : function_data_) {
1223 auto pcie_type = std_to_attr<>(
1224 std::pair<std::string, int>("pcie_type", types::PCIE_Type_Cfg));
1225 auto forward_target = std_to_attr<>(
1226 std::pair<std::string, ConfObjectRef>("forward_target",
1227 gasket_obj_));
1228 auto function_id = std_to_attr<>(
1229 std::pair<std::string, int>("function_id", fd.first));
1230 attr_value_t attr = SIM_make_attr_list(3, pcie_type, forward_target,
1231 function_id);
1232
1233 auto obj_name = gasket_obj_.name() + ".port.cfg" + \
1234 std::to_string(fd.first);
1235 auto *o = SIM_get_object(obj_name.c_str());
1236 if (o == nullptr) {
1237 std::ignore = SIM_clear_exception();
1238 o = SIM_create_object(map_helper, obj_name.c_str(), attr);
1239 }
1240 assert(o);
1241 SIM_attr_free(&attr);
1242 }
1243}
1244
1245template <unsigned int BUSWIDTH, typename TYPES>
1247getCfgMapHelper(uint16_t device_id) {
1248 std::string name = "port.cfg";
1249 if (function_number_has_8bits_) {
1250 name += std::to_string(device_id & 0xff);
1251 } else {
1252 name += std::to_string(device_id & 7);
1253 }
1254 auto map_helper = SIM_object_descendant(gasket_obj_, name.c_str());
1255 if (!map_helper) {
1256 SIM_LOG_CRITICAL(simulation_obj_, Log_Configuration,
1257 "Could not find map helper object with name '%s.%s'",
1258 gasket_obj_.name().c_str(), name.c_str());
1259 }
1260 return map_helper;
1261}
1262
1263template <unsigned int BUSWIDTH, typename TYPES>
1264size_t PcieMappingInterconnect<BUSWIDTH, TYPES>::
1265validateTransaction(tlm::tlm_generic_payload &trans) { // NOLINT
1266 PcieTlmExtension *pcie_ext = nullptr;
1267 trans.get_extension<PcieTlmExtension>(pcie_ext);
1268
1269 if (pcie_ext == nullptr) {
1270 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
1271 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
1272 "PcieTlmExtension is required but not found");
1273 return 0;
1274 }
1275
1276 std::ostringstream os;
1277 os << "Received a PCIe transaction with type "
1278 << pcieTypeName(pcie_ext->type);
1279 if (pcie_ext->device_id_set) {
1280 os << ", " << pcieDeviceIdStr(pcie_ext->device_id);
1281 }
1282 os << ", address 0x" << std::hex << trans.get_address() << std::endl;
1283 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
1284
1285 if (pcie_ext->type == types::PCIE_Type_Not_Set
1286 || pcie_ext->type == types::PCIE_Type_Other) {
1287 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
1288 "Only support following PCIe types: MEM/IO/CFG/MSG");
1289 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
1290 return 0;
1291 }
1292
1293 if (pcie_ext->type == types::PCIE_Type_Cfg
1294 && !pcie_ext->device_id_set) {
1295 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
1296 "PCIe device ID ATOM is required");
1297 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
1298 return 0;
1299 }
1300
1301 size_t socket_id = 0;
1302 try {
1303 // For ARI (function numbers > 7) use 8-bit function extraction,
1304 // otherwise mask to the standard 3-bit function field.
1305 const uint16_t fn = function_number_has_8bits_
1306 ? (pcie_ext->device_id & 0xFF)
1307 : (pcie_ext->device_id & 7);
1308 socket_id = findSocketId(pcie_ext->type, fn, trans);
1309 } catch (const std::exception &e) {
1310 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, e.what());
1311 trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
1312 return 0;
1313 }
1314
1315 if (socket_id >= initiator_sockets_.size()) {
1316 os.clear();
1317 os << "Initiator socket ID " << socket_id << " not created yet";
1318 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
1319 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
1320 return 0;
1321 }
1322
1323 if (pcie_ext->type == types::PCIE_Type_Cfg) {
1324 interceptCfgHeaderAccess(socket_id, trans);
1325 }
1326
1327 return socket_id;
1328}
1329
1330template <unsigned int BUSWIDTH, typename TYPES>
1331void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1332transaction_b_transport(tlm::tlm_generic_payload &trans, // NOLINT: SystemC API
1333 sc_core::sc_time &t) { // NOLINT: SystemC API
1334 trans.set_response_status(tlm::TLM_OK_RESPONSE);
1335 size_t socket_id = validateTransaction(trans);
1336 if (trans.get_response_status() == tlm::TLM_OK_RESPONSE) {
1337 initiator_sockets_.at(socket_id)->b_transport(trans, t);
1338 }
1339}
1340
1341template <unsigned int BUSWIDTH, typename TYPES>
1342unsigned int PcieMappingInterconnect<BUSWIDTH, TYPES>::
1343transaction_transport_dbg(tlm::tlm_generic_payload &trans) { // NOLINT
1344 trans.set_response_status(tlm::TLM_OK_RESPONSE);
1345 size_t socket_id = validateTransaction(trans);
1346 if (trans.get_response_status() == tlm::TLM_OK_RESPONSE) {
1347 return initiator_sockets_.at(socket_id)->transport_dbg(trans);
1348 } else {
1349 return 0;
1350 }
1351}
1352
1353/*
1354 * For pcie-map, just relay the incoming BW transaction to target socket
1355 */
1356template <unsigned int BUSWIDTH, typename TYPES>
1357void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1358pcie_map_invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
1359 sc_dt::uint64 end_range) {
1360 pcie_map_target_socket_->invalidate_direct_mem_ptr(start_range,
1361 end_range);
1362}
1363
1364/*
1365 * For pcie-map, just relay the incoming transaction to outgoing socket
1366 */
1367template <unsigned int BUSWIDTH, typename TYPES>
1368void PcieMappingInterconnect<BUSWIDTH, TYPES>::pcie_map_b_transport(
1369 tlm::tlm_generic_payload &trans, // NOLINT
1370 sc_core::sc_time &t) { // NOLINT
1371 pcie_map_initiator_socket->b_transport(trans, t);
1372}
1373
1374/*
1375 * For pcie-map, just relay the incoming transaction to outgoing socket
1376 */
1377template <unsigned int BUSWIDTH, typename TYPES>
1378unsigned int PcieMappingInterconnect<BUSWIDTH, TYPES>::pcie_map_transport_dbg(
1379 tlm::tlm_generic_payload &trans) { // NOLINT
1380 return pcie_map_initiator_socket->transport_dbg(trans);
1381}
1382
1383/*
1384 * For pcie-map, just relay the incoming transaction to outgoing socket
1385 */
1386template <unsigned int BUSWIDTH, typename TYPES>
1387bool PcieMappingInterconnect<BUSWIDTH, TYPES>::pcie_map_get_direct_mem_ptr(
1388 tlm::tlm_generic_payload &trans, // NOLINT
1389 tlm::tlm_dmi &dmi_data) { // NOLINT
1390 return pcie_map_initiator_socket->get_direct_mem_ptr(trans, dmi_data);
1391}
1392
1393template <unsigned int BUSWIDTH, typename TYPES>
1394void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1395castToTarget(sc_core::sc_object *object, supported_target_socket_t *target) {
1396 if (object == nullptr) {
1397 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
1398 "Unable to castToTarget from a nullptr");
1399 return;
1400 }
1401
1402 auto *tlm_target = dynamic_cast<
1403 tlm::tlm_target_socket<BUSWIDTH, TYPES> *>(object);
1404 if (tlm_target) {
1405 target->tlm = tlm_target;
1406 return;
1407 }
1408
1409 auto *multi_target = dynamic_cast<
1410 tlm_utils::multi_target_base<BUSWIDTH, TYPES> *>(object);
1411 if (multi_target) {
1412 target->multi = multi_target;
1413 return;
1414 }
1415
1416 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
1417 "Unable to dynamic-cast PCIe target socket");
1418}
1419
1420} // namespace composite
1421} // namespace systemc
1422} // namespace simics
1423
1424#endif
void send_extension(iface::Transaction *transaction) override
Called by extension after the extension is set on the payload.
Definition: pcie_mapping_interconnect.h:88
virtual ~PcieMappingInterconnectExtensionSender()
Definition: pcie_mapping_interconnect.h:85
iface::Transaction failed_transaction_
Definition: pcie_mapping_interconnect.h:97
void send_failed(iface::Transaction *transaction) override
Called by extension if method_call invocation was missing.
Definition: pcie_mapping_interconnect.h:92
Definition: pcie_mapping_interconnect.h:107
PcieMappingInterconnect(sc_core::sc_module_name="PcieMappingInterconnect")
Definition: pcie_mapping_interconnect.h:122
void connected(uint16_t device_id)
Definition: pcie_mapping_interconnect.h:436
void before_end_of_elaboration() override
Definition: pcie_mapping_interconnect.h:393
bool enable_base_address_subtraction
Definition: pcie_mapping_interconnect.h:209
initiator_socket_t pcie_map_initiator_socket
IC -> Simics, forwards the upstream pcie-map transactions.
Definition: pcie_mapping_interconnect.h:204
void connect(iface::PcieDeviceQueryInterface *pci, iface::PcieBaseAddressRegisterQueryInterface *bar, iface::PcieResetInterface *reset, ConfObjectRef o, iface::PcieSriovQueryInterface *sriov=nullptr)
Called by (outer) composite class to retrieve the required information to connect the IC with the PCI...
Definition: pcie_mapping_interconnect.h:562
void hotReset()
Definition: pcie_mapping_interconnect.h:481
void createCfgMapHelper()
Definition: pcie_mapping_interconnect.h:1180
target_socket_t pcie_device_target_socket
Definition: pcie_mapping_interconnect.h:202
std::map< std::pair< size_t, size_t >, size_t > addressIdMemMap() const
Definition: pcie_mapping_interconnect.h:629
target_socket_t transaction_target_socket
Definition: pcie_mapping_interconnect.h:200
void disconnected(uint16_t device_id)
Definition: pcie_mapping_interconnect.h:471
sc_core::sc_in< bool > warm_reset_pin
Definition: pcie_mapping_interconnect.h:206
std::map< std::pair< size_t, size_t >, size_t > addressIdIoMap() const
Definition: pcie_mapping_interconnect.h:635
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:51
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:30
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:93
Interface providing static SR-IOV configuration to the PcieMappingInterconnect.
Definition: pcie_device_query_interface.h:124
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