[#69489] co-simulation: Allow multiple peripherals per renode module
diff --git a/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedCFU.cs b/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedCFU.cs
index 79faba6..57f5bfd 100644
--- a/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedCFU.cs
+++ b/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedCFU.cs
@@ -26,6 +26,10 @@
     {
         public CoSimulatedCFU(Machine machine, long frequency = 0, ulong limitBuffer = LimitBuffer, int timeout = DefaultTimeout, string simulationFilePathLinux = null, string simulationFilePathWindows = null, string simulationFilePathMacOS = null)
         {
+            // Multiple CoSimulatedCFUs per CoSimulationConnection are currently not supported.
+            RenodeToCosimIndex = 0;
+            CosimToRenodeIndex = 0;
+
             connection = new CoSimulationConnection(machine, "cosimulation_connection", frequency,
                     simulationFilePathLinux, simulationFilePathWindows, simulationFilePathMacOS,
                     null, null, null,
@@ -265,6 +269,9 @@
             }
         }
 
+        public int RenodeToCosimIndex { get; }
+        public int CosimToRenodeIndex { get; }
+
         protected const ulong LimitBuffer = 100000;
         protected const int DefaultTimeout = 3000;
         private NativeBinder executeBinder;
diff --git a/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedCPU.cs b/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedCPU.cs
index 6b76203..57feec1 100644
--- a/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedCPU.cs
+++ b/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedCPU.cs
@@ -35,6 +35,10 @@
             string simulationContextLinux = null, string simulationContextWindows = null, string simulationContextMacOS = null, string address = null)
             : base(0, cpuType, machine, endianness, bitness)
         {
+            // Multiple CoSimulatedCPUs per CoSimulationConnection are currently not supported.
+            RenodeToCosimIndex = 0;
+            CosimToRenodeIndex = 0;
+
             cosimConnection = new CoSimulationConnection(machine, "cpu_cosim_cosimConnection", 0,
                         simulationFilePathLinux, simulationFilePathWindows, simulationFilePathMacOS,
                         simulationContextLinux, simulationContextWindows, simulationContextMacOS,
@@ -83,7 +87,7 @@
             this.NoisyLog("IRQ {0}, value {1}", number, value);
             lock(cosimConnectionLock)
             {
-                cosimConnection.Send(ActionType.Interrupt, (ulong)number, (ulong)(value ? 1 : 0));
+                cosimConnection.Send(this, ActionType.Interrupt, (ulong)number, (ulong)(value ? 1 : 0));
             }
         }
 
@@ -92,7 +96,7 @@
             lock(cosimConnectionLock)
             {
                 setRegisterValue = false;
-                cosimConnection.Send(ActionType.RegisterSet, (ulong)register, (ulong) value);
+                cosimConnection.Send(this, ActionType.RegisterSet, (ulong)register, (ulong) value);
                 while(!setRegisterValue) // This kind of while loops are for socket communication
                 {
                     cosimConnection.HandleMessage();
@@ -105,7 +109,7 @@
             lock(cosimConnectionLock)
             {
                 gotRegisterValue = false;
-                cosimConnection.Send(ActionType.RegisterGet, (ulong)register, 0);
+                cosimConnection.Send(this, ActionType.RegisterGet, (ulong)register, 0);
                 while(!gotRegisterValue)
                 {
                     cosimConnection.HandleMessage();
@@ -127,7 +131,7 @@
                         while(instructionsExecutedThisRound < 1)
                         {
                             gotStep = false;
-                            cosimConnection.Send(ActionType.Step, 0, 1);
+                            cosimConnection.Send(this, ActionType.Step, 0, 1);
                             while(!gotStep)
                             {
                                cosimConnection.HandleMessage();
@@ -137,7 +141,7 @@
                     else
                     {
                         ticksProcessed = false;
-                        cosimConnection.Send(ActionType.TickClock, 0, numberOfInstructionsToExecute);
+                        cosimConnection.Send(this, ActionType.TickClock, 0, numberOfInstructionsToExecute);
                         while(!ticksProcessed)
                         {
                             cosimConnection.HandleMessage();
@@ -190,10 +194,10 @@
                         switch(executionMode)
                         {
                             case ExecutionMode.Continuous:
-                                cosimConnection.Send(ActionType.SingleStepMode, 0, 0);
+                                cosimConnection.Send(this, ActionType.SingleStepMode, 0, 0);
                                 break;
                             case ExecutionMode.SingleStep:
-                                cosimConnection.Send(ActionType.SingleStepMode, 0, 1);
+                                cosimConnection.Send(this, ActionType.SingleStepMode, 0, 1);
                                 break;
                         }
 
@@ -291,6 +295,9 @@
             }
         }
 
+        public int RenodeToCosimIndex { get; }
+        public int CosimToRenodeIndex { get; }
+
         protected readonly object cosimConnectionLock = new object();
 
         private readonly CoSimulationConnection cosimConnection;
diff --git a/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedPeripheral.cs b/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedPeripheral.cs
index 8963921..718bf5c 100644
--- a/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedPeripheral.cs
+++ b/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedPeripheral.cs
@@ -25,12 +25,14 @@
             string simulationFilePathLinux = null, string simulationFilePathWindows = null, string simulationFilePathMacOS = null,
             string simulationContextLinux = null, string simulationContextWindows = null, string simulationContextMacOS = null,
             ulong limitBuffer = LimitBuffer, int timeout = DefaultTimeout, string address = null,  bool createConnection = true,
-            ulong renodeToCosimSignalsOffset = 0, Range? cosimToRenodeSignalRange = null)
+            ulong renodeToCosimSignalsOffset = 0, Range? cosimToRenodeSignalRange = null, int renodeToCosimIndex = 0, int cosimToRenodeIndex = 0)
         {
             UseAbsoluteAddress = useAbsoluteAddress;
             this.maxWidth = maxWidth;
             this.renodeToCosimSignalsOffset = renodeToCosimSignalsOffset;
             this.cosimToRenodeSignalRange = cosimToRenodeSignalRange;
+            RenodeToCosimIndex = renodeToCosimIndex;
+            CosimToRenodeIndex = cosimToRenodeIndex;
 
             if(createConnection)
             {
@@ -90,7 +92,7 @@
             {
                 return 0;
             }
-            return (byte)connection.Read(ActionType.ReadFromBusByte, offset);
+            return (byte)connection.Read(this, ActionType.ReadFromBusByte, offset);
         }
 
         public ushort ReadWord(long offset)
@@ -100,7 +102,7 @@
             {
                 return 0;
             }
-            return (ushort)connection.Read(ActionType.ReadFromBusWord, offset);
+            return (ushort)connection.Read(this, ActionType.ReadFromBusWord, offset);
         }
 
         public uint ReadDoubleWord(long offset)
@@ -110,7 +112,7 @@
             {
                 return 0;
             }
-            return (uint)connection.Read(ActionType.ReadFromBusDoubleWord, offset);
+            return (uint)connection.Read(this, ActionType.ReadFromBusDoubleWord, offset);
         }
 
         public ulong ReadQuadWord(long offset)
@@ -120,7 +122,7 @@
             {
                 return 0;
             }
-            return connection.Read(ActionType.ReadFromBusQuadWord, offset);
+            return connection.Read(this, ActionType.ReadFromBusQuadWord, offset);
         }
 
         public void WriteByte(long offset, byte value)
@@ -128,7 +130,7 @@
             offset = UseAbsoluteAddress ? (long)absoluteAddress : offset;
             if(VerifyLength(8, offset, value))
             {
-                connection.Write(ActionType.WriteToBusByte, offset, value);
+                connection.Write(this, ActionType.WriteToBusByte, offset, value);
             }
         }
 
@@ -137,7 +139,7 @@
             offset = UseAbsoluteAddress ? (long)absoluteAddress : offset;
             if(VerifyLength(16, offset, value))
             {
-                connection.Write(ActionType.WriteToBusWord, offset, value);
+                connection.Write(this, ActionType.WriteToBusWord, offset, value);
             }
         }
 
@@ -146,7 +148,7 @@
             offset = UseAbsoluteAddress ? (long)absoluteAddress : offset;
             if(VerifyLength(32, offset, value))
             {
-                connection.Write(ActionType.WriteToBusDoubleWord, offset, value);
+                connection.Write(this, ActionType.WriteToBusDoubleWord, offset, value);
             }
         }
 
@@ -155,7 +157,7 @@
             offset = UseAbsoluteAddress ? (long)absoluteAddress : offset;
             if(VerifyLength(64, offset, value))
             {
-                connection.Write(ActionType.WriteToBusQuadWord, offset, value);
+                connection.Write(this, ActionType.WriteToBusQuadWord, offset, value);
             }
         }
 
@@ -295,6 +297,9 @@
             }
         }
 
+        public int RenodeToCosimIndex { get; }
+        public int CosimToRenodeIndex { get; }
+
         protected CoSimulationConnection connection;
         protected const ulong LimitBuffer = 1000000;
         protected const int DefaultTimeout  = 3000;
diff --git a/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedUART.cs b/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedUART.cs
index 4e580d6..5442bc1 100644
--- a/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedUART.cs
+++ b/src/Plugins/CoSimulationPlugin/CoSimulated/Peripherals/CoSimulatedUART.cs
@@ -27,14 +27,14 @@
                     simulationFilePathLinux, simulationFilePathWindows, simulationFilePathMacOS,
                     simulationContextLinux, simulationContextWindows, simulationContextMacOS,
                     limitBuffer, timeout, address, createConnection, renodeToCosimSignalsOffset,
-                    cosimToRenodeSignalRange)
+                    cosimToRenodeSignalRange, 0, 0)
         {
             IRQ = new GPIO();
         }
 
         public void WriteChar(byte value)
         {
-            connection.Send((ActionType)UARTActionNumber.UARTRxd, 0, value);
+            connection.Send(this, (ActionType)UARTActionNumber.UARTRxd, 0, value);
         }
 
         public bool HandleReceivedMessage(ProtocolMessage message)
diff --git a/src/Plugins/CoSimulationPlugin/Connection/CoSimulationConnection.cs b/src/Plugins/CoSimulationPlugin/Connection/CoSimulationConnection.cs
index 11dec20..4368951 100644
--- a/src/Plugins/CoSimulationPlugin/Connection/CoSimulationConnection.cs
+++ b/src/Plugins/CoSimulationPlugin/Connection/CoSimulationConnection.cs
@@ -12,6 +12,7 @@
 using Antmicro.Renode.Exceptions;
 using Antmicro.Renode.Logging;
 using Antmicro.Renode.Peripherals;
+using Antmicro.Renode.Peripherals.Bus;
 using Antmicro.Renode.Peripherals.CPU;
 using Antmicro.Renode.Peripherals.CoSimulated;
 using Antmicro.Renode.Peripherals.Timers;
@@ -72,16 +73,24 @@
 
             RegisterInHostMachine(name);
             cosimConnection = SetupConnection(address, timeout, frequency, limitBuffer);
+
+            cosimIdxToPeripheral = new Dictionary<int, ICoSimulationConnectible>();
         }
 
         public void AttachTo(ICoSimulationConnectible peripheral)
         {
+            if(cosimIdxToPeripheral.ContainsKey(peripheral.CosimToRenodeIndex))
+            {
+                throw new RecoverableException("Failed to add a peripheral to co-simulated connection. Make sure all connected peripherals have correctly assigned, unique cosimulation subordinate and manager indices in platform definition.");
+            }
+            cosimIdxToPeripheral.Add(peripheral.CosimToRenodeIndex, peripheral);
             peripheral.OnConnectionAttached(this);
         }
 
         public void DetachFrom(ICoSimulationConnectible peripheral)
         {
             peripheral.OnConnectionDetached(this);
+            cosimIdxToPeripheral.Remove(peripheral.CosimToRenodeIndex);
         }
 
         public void Dispose()
@@ -205,17 +214,18 @@
             cosimConnection.Connect();
         }
 
-        public void Send(ActionType actionId, ulong offset, ulong value)
+        public void Send(ICoSimulationConnectible connectible, ActionType actionId, ulong offset, ulong value)
         {
-            if(!cosimConnection.TrySendMessage(new ProtocolMessage(actionId, offset, value)))
+            int renodeToCosimIndex = connectible != null ? connectible.RenodeToCosimIndex : ProtocolMessage.NoPeripheralIndex;
+            if(!cosimConnection.TrySendMessage(new ProtocolMessage(actionId, offset, value, renodeToCosimIndex)))
             {
                 AbortAndLogError("Send error!");
             }
         }
 
-        public void Respond(ActionType actionId, ulong offset, ulong value)
+        public void Respond(ActionType actionId, ulong offset, ulong value, int peripheralIdx)
         {
-            if(!cosimConnection.TryRespond(new ProtocolMessage(actionId, offset, value)))
+            if(!cosimConnection.TryRespond(new ProtocolMessage(actionId, offset, value, peripheralIdx)))
             {
                 AbortAndLogError("Respond error!");
             }
@@ -231,12 +241,12 @@
             {
                 timer.Reset();
             }
-            Send(ActionType.ResetPeripheral, 0, 0);
+            Send(null, ActionType.ResetPeripheral, 0, 0);
         }
 
         public void SendGPIO(int number, bool value)
         {
-            Write(ActionType.Interrupt, number, value ? 1ul : 0ul);
+            Write(null, ActionType.Interrupt, number, value ? 1ul : 0ul);
         }
 
         public string ConnectionParameters => (cosimConnection as SocketConnection)?.ConnectionParameters ?? "";
@@ -261,25 +271,25 @@
             gpioEntries.RemoveAll(entry => entry.range.Equals(translationRange));
         }
 
-        public void Write(ActionType type, long offset, ulong value)
+        public void Write(ICoSimulationConnectible connectible, ActionType type, long offset, ulong value)
         {
             if(!IsConnected)
             {
                 this.Log(LogLevel.Warning, "Cannot write to peripheral. Set SimulationFilePath or connect to a simulator first!");
                 return;
             }
-            Send(type, (ulong)offset, value);
+            Send(connectible, type, (ulong)offset, value);
             ValidateResponse(Receive());
         }
 
-        public ulong Read(ActionType type, long offset)
+        public ulong Read(ICoSimulationConnectible connectible, ActionType type, long offset)
         {
             if(!IsConnected)
             {
                 this.Log(LogLevel.Warning, "Cannot read from peripheral. Set SimulationFilePath or connect to a simulator first!");
                 return 0;
             }
-            Send(type, (ulong)offset, 0);
+            Send(connectible, type, (ulong)offset, 0);
             var result = Receive();
             ValidateResponse(result);
 
@@ -321,7 +331,7 @@
                 timer = new LimitTimer(machine.ClockSource, frequency, null, LimitTimerName, limitBuffer, enabled: true, eventEnabled: true, autoUpdate: true);
                 timer.LimitReached += () =>
                 {
-                    if(!cosimConnection.TrySendMessage(new ProtocolMessage(ActionType.TickClock, 0, limitBuffer)))
+                    if(!cosimConnection.TrySendMessage(new ProtocolMessage(ActionType.TickClock, 0, limitBuffer, ProtocolMessage.NoPeripheralIndex)))
                     {
                         AbortAndLogError("Send error!");
                     }
@@ -357,6 +367,13 @@
 
         private void HandleReceivedMessage(ProtocolMessage message)
         {
+            ICoSimulationConnectible peripheral = null;
+            if(message.PeripheralIndex != ProtocolMessage.NoPeripheralIndex && !cosimIdxToPeripheral.TryGetValue(message.PeripheralIndex, out peripheral))
+            {
+                this.Log(LogLevel.Error, "Received co-simulation message {} to a peripheral with index {}, not registered in Renode. Make sure \"cosimToRenodeIndex\" property is provided in platform definition. Message will be ignored.", message.ActionId, message.PeripheralIndex);
+                return;
+            }
+
             if(OnReceive != null)
             {
                 foreach(OnReceiveDelegate or in OnReceive.GetInvocationList())
@@ -368,6 +385,13 @@
                 }
             }
 
+            IBusController systemBus = machine.SystemBus;
+            var busPeripheral = peripheral as IBusPeripheral;
+            if(busPeripheral != null)
+            {
+                systemBus = machine.GetSystemBus(busPeripheral);
+            }
+
             switch(message.ActionId)
             {
                 case ActionType.InvalidAction:
@@ -378,39 +402,39 @@
                     break;
                 case ActionType.PushByte:
                     this.Log(LogLevel.Noisy, "Writing byte: 0x{0:X} to address: 0x{1:X}", message.Data, message.Address);
-                    machine.SystemBus.WriteByte(message.Address, (byte)message.Data);
-                    Respond(ActionType.PushConfirmation, 0, 0);
+                    systemBus.WriteByte(message.Address, (byte)message.Data);
+                    Respond(ActionType.PushConfirmation, 0, 0, message.PeripheralIndex);
                     break;
                 case ActionType.PushWord:
                     this.Log(LogLevel.Noisy, "Writing word: 0x{0:X} to address: 0x{1:X}", message.Data, message.Address);
-                    machine.SystemBus.WriteWord(message.Address, (ushort)message.Data);
-                    Respond(ActionType.PushConfirmation, 0, 0);
+                    systemBus.WriteWord(message.Address, (ushort)message.Data);
+                    Respond(ActionType.PushConfirmation, 0, 0, message.PeripheralIndex);
                     break;
                 case ActionType.PushDoubleWord:
                     this.Log(LogLevel.Noisy, "Writing double word: 0x{0:X} to address: 0x{1:X}", message.Data, message.Address);
-                    machine.SystemBus.WriteDoubleWord(message.Address, (uint)message.Data);
-                    Respond(ActionType.PushConfirmation, 0, 0);
+                    systemBus.WriteDoubleWord(message.Address, (uint)message.Data);
+                    Respond(ActionType.PushConfirmation, 0, 0, message.PeripheralIndex);
                     break;
                 case ActionType.PushQuadWord:
                     this.Log(LogLevel.Noisy, "Writing quad word: 0x{0:X} to address: 0x{1:X}", message.Data, message.Address);
-                    machine.SystemBus.WriteQuadWord(message.Address, message.Data);
-                    Respond(ActionType.PushConfirmation, 0, 0);
+                    systemBus.WriteQuadWord(message.Address, message.Data);
+                    Respond(ActionType.PushConfirmation, 0, 0, message.PeripheralIndex);
                     break;
                 case ActionType.GetByte:
                     this.Log(LogLevel.Noisy, "Requested byte from address: 0x{0:X}", message.Address);
-                    Respond(ActionType.WriteToBus, 0, machine.SystemBus.ReadByte(message.Address));
+                    Respond(ActionType.WriteToBus, 0, systemBus.ReadByte(message.Address), message.PeripheralIndex);
                     break;
                 case ActionType.GetWord:
                     this.Log(LogLevel.Noisy, "Requested word from address: 0x{0:X}", message.Address);
-                    Respond(ActionType.WriteToBus, 0, machine.SystemBus.ReadWord(message.Address));
+                    Respond(ActionType.WriteToBus, 0, systemBus.ReadWord(message.Address), message.PeripheralIndex);
                     break;
                 case ActionType.GetDoubleWord:
                     this.Log(LogLevel.Noisy, "Requested double word from address: 0x{0:X}", message.Address);
-                    Respond(ActionType.WriteToBus, 0, machine.SystemBus.ReadDoubleWord(message.Address));
+                    Respond(ActionType.WriteToBus, 0, systemBus.ReadDoubleWord(message.Address), message.PeripheralIndex);
                     break;
                 case ActionType.GetQuadWord:
                     this.Log(LogLevel.Noisy, "Requested quad word from address: 0x{0:X}", message.Address);
-                    Respond(ActionType.WriteToBus, 0, machine.SystemBus.ReadQuadWord(message.Address));
+                    Respond(ActionType.WriteToBus, 0, systemBus.ReadQuadWord(message.Address), message.PeripheralIndex);
                     break;
                 case ActionType.TickClock:
                     allTicksProcessedARE.Set();
@@ -476,6 +500,8 @@
         private readonly ICoSimulationConnection cosimConnection;
 
         private const int DefaultTimeout = 3000;
+
+        private readonly Dictionary<int, ICoSimulationConnectible> cosimIdxToPeripheral;
         private string simulationFilePath;
         private IMachine machine;
         private volatile bool disposeInitiated;
diff --git a/src/Plugins/CoSimulationPlugin/Connection/ICoSimulationConnectible.cs b/src/Plugins/CoSimulationPlugin/Connection/ICoSimulationConnectible.cs
index fe59647..4084279 100644
--- a/src/Plugins/CoSimulationPlugin/Connection/ICoSimulationConnectible.cs
+++ b/src/Plugins/CoSimulationPlugin/Connection/ICoSimulationConnectible.cs
@@ -13,5 +13,7 @@
     {
         void OnConnectionAttached(CoSimulationConnection connection);
         void OnConnectionDetached(CoSimulationConnection connection);
+        int RenodeToCosimIndex { get; }
+        int CosimToRenodeIndex { get; }
     }
 }
diff --git a/src/Plugins/CoSimulationPlugin/Connection/Protocols/ProtocolMessage.cs b/src/Plugins/CoSimulationPlugin/Connection/Protocols/ProtocolMessage.cs
index fb2d69f..317877a 100644
--- a/src/Plugins/CoSimulationPlugin/Connection/Protocols/ProtocolMessage.cs
+++ b/src/Plugins/CoSimulationPlugin/Connection/Protocols/ProtocolMessage.cs
@@ -12,11 +12,12 @@
     [StructLayout(LayoutKind.Sequential, Pack = 2)]
     public struct ProtocolMessage
     {
-        public ProtocolMessage(ActionType actionId, ulong address, ulong data)
+        public ProtocolMessage(ActionType actionId, ulong address, ulong data, int peripheralIndex)
         {
             this.ActionId = actionId;
             this.Address = address;
             this.Data = data;
+            this.PeripheralIndex = peripheralIndex;
         }
 
         public byte[] Serialize()
@@ -61,5 +62,9 @@
         public ActionType ActionId { get; set; }
         public ulong Address { get; set; }
         public ulong Data { get; set; }
+        public int PeripheralIndex { get; set; }
+
+        // Peripheral index used for messages that are not associated with any peripherals.
+        public const int NoPeripheralIndex = -1;
     }
 }
diff --git a/src/Plugins/CoSimulationPlugin/Connection/SocketConnection.cs b/src/Plugins/CoSimulationPlugin/Connection/SocketConnection.cs
index b793e5c..ee8bb25 100644
--- a/src/Plugins/CoSimulationPlugin/Connection/SocketConnection.cs
+++ b/src/Plugins/CoSimulationPlugin/Connection/SocketConnection.cs
@@ -150,7 +150,7 @@
             if(IsConnected)
             {
                 parentElement.DebugLog("Sending 'Disconnect' message to close peripheral gracefully...");
-                TrySendMessage(new ProtocolMessage(ActionType.Disconnect, 0, 0));
+                TrySendMessage(new ProtocolMessage(ActionType.Disconnect, 0, 0, ProtocolMessage.NoPeripheralIndex));
                 mainSocketComunicator.CancelCommunication();
             }
 
@@ -305,9 +305,23 @@
 
         private bool TryHandshake()
         {
-            return TrySendMessage(new ProtocolMessage(ActionType.Handshake, 0, 0))
-                   && TryReceiveMessage(out var result)
-                   && result.ActionId == ActionType.Handshake;
+            if(!TrySendMessage(new ProtocolMessage(ActionType.Handshake, 0, 0, ProtocolMessage.NoPeripheralIndex)))
+            {
+                parentElement.Log(LogLevel.Error, "Failed to send handshake message to co-simulation.");
+                return false;
+            }
+            if(!TryReceiveMessage(out var result))
+            {
+                parentElement.Log(LogLevel.Error, "Failed to receive handshake response from co-simulation.");
+                return false;
+            }
+            if(result.ActionId != ActionType.Handshake)
+            {
+                parentElement.Log(LogLevel.Error, "Invalid handshake response received from co-simulation.");
+                return false;
+            }
+
+            return true;
         }
 
         private void HandleReceived(ProtocolMessage message)
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/ahb/renode_ahb_manager.sv b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/ahb/renode_ahb_manager.sv
index 0e1b2fb..9fd3ac0 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/ahb/renode_ahb_manager.sv
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/ahb/renode_ahb_manager.sv
@@ -8,9 +8,9 @@
 
 `timescale 1ns / 1ps
 
-import renode_pkg::renode_runtime, renode_pkg::LogWarning;
+import renode_pkg::renode_runtime, renode_pkg::bus_connection, renode_pkg::LogWarning;
 
-module renode_ahb_manager (
+module renode_ahb_manager #(int RenodeToCosimIndex = 0) (
     ref renode_runtime runtime,
     renode_ahb_if bus
 );
@@ -20,33 +20,33 @@
   typedef logic [bus.DataWidth-1:0] data_t;
   wire clk = bus.hclk;
 
-  always @(runtime.controller.reset_assert_request) begin
+  always @(runtime.controllers[RenodeToCosimIndex].reset_assert_request) begin
     bus.hresetn = 0;
     bus.haddr   = '0;
     bus.htrans  = Idle;
     repeat (2) @(posedge clk);
-    runtime.controller.reset_assert_respond();
+    runtime.controllers[RenodeToCosimIndex].reset_assert_respond();
   end
 
-  always @(runtime.controller.reset_deassert_request) begin
+  always @(runtime.controllers[RenodeToCosimIndex].reset_deassert_request) begin
     bus.hresetn = 1;
     repeat (2) @(posedge clk);
-    runtime.controller.reset_deassert_respond();
+    runtime.controllers[RenodeToCosimIndex].reset_deassert_respond();
   end
 
-  always @(runtime.controller.read_transaction_request) read_transaction();
-  always @(runtime.controller.write_transaction_request) write_transaction();
+  always @(runtime.controllers[RenodeToCosimIndex].read_transaction_request) read_transaction();
+  always @(runtime.controllers[RenodeToCosimIndex].write_transaction_request) write_transaction();
 
   task static write_transaction();
     renode_pkg::valid_bits_e valid_bits;
     data_t data;
     bit is_invalid;
 
-    valid_bits = runtime.controller.write_transaction_data_bits;
-    data = data_t'(runtime.controller.write_transaction_data & valid_bits);
-    configure_transfer(runtime.controller.write_transaction_address, valid_bits, Write, is_invalid);
+    valid_bits = runtime.controllers[RenodeToCosimIndex].write_transaction_data_bits;
+    data = data_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_data & valid_bits);
+    configure_transfer(runtime.controllers[RenodeToCosimIndex].write_transaction_address, valid_bits, Write, is_invalid);
     if (is_invalid) begin
-        runtime.controller.write_respond(is_invalid);
+        runtime.controllers[RenodeToCosimIndex].write_respond(is_invalid);
         return;
     end
 
@@ -55,7 +55,7 @@
     bus.htrans <= Idle;
 
     do @(posedge clk); while (!bus.hready);
-    runtime.controller.write_respond(is_response_error(bus.hresp));
+    runtime.controllers[RenodeToCosimIndex].write_respond(is_response_error(bus.hresp));
   endtask
 
   task static read_transaction();
@@ -64,10 +64,10 @@
     bit is_invalid;
     bit is_error;
 
-    valid_bits = runtime.controller.read_transaction_data_bits;
-    configure_transfer(runtime.controller.read_transaction_address, valid_bits, Read, is_invalid);
+    valid_bits = runtime.controllers[RenodeToCosimIndex].read_transaction_data_bits;
+    configure_transfer(runtime.controllers[RenodeToCosimIndex].read_transaction_address, valid_bits, Read, is_invalid);
     if (is_invalid) begin
-        runtime.controller.read_respond(renode_pkg::data_t'(0), is_invalid);
+        runtime.controllers[RenodeToCosimIndex].read_respond(renode_pkg::data_t'(0), is_invalid);
         return;
     end
 
@@ -76,7 +76,7 @@
     do @(posedge clk); while (!bus.hready);
     data = bus.hrdata;
     is_error = is_response_error(bus.hresp);
-    runtime.controller.read_respond(renode_pkg::data_t'(data) & valid_bits, is_error);
+    runtime.controllers[RenodeToCosimIndex].read_respond(renode_pkg::data_t'(data) & valid_bits, is_error);
   endtask
 
   task static configure_transfer(renode_pkg::address_t address, renode_pkg::valid_bits_e valid_bits, transfer_direction_e direction, output logic is_invalid);
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/ahb/renode_ahb_subordinate.sv b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/ahb/renode_ahb_subordinate.sv
index a572687..c183c6b 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/ahb/renode_ahb_subordinate.sv
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/ahb/renode_ahb_subordinate.sv
@@ -10,7 +10,7 @@
 
 import renode_pkg::renode_runtime, renode_pkg::LogWarning;
 
-module renode_ahb_subordinate (
+module renode_ahb_subordinate #(int CosimToRenodeIndex = 0) (
     ref renode_runtime runtime,
     renode_ahb_if bus
 );
@@ -20,18 +20,18 @@
   typedef logic [bus.DataWidth-1:0] data_t;
   wire clk = bus.hclk;
 
-  always @(runtime.peripheral.reset_assert_request) begin
+  always @(runtime.peripherals[CosimToRenodeIndex].reset_assert_request) begin
     bus.hresetn = 0;
     bus.hresp = Okay;
     bus.hreadyout = 1;
     repeat (2) @(posedge clk);
-    runtime.peripheral.reset_assert_respond();
+    runtime.peripherals[CosimToRenodeIndex].reset_assert_respond();
   end
 
-  always @(runtime.peripheral.reset_deassert_request) begin
+  always @(runtime.peripherals[CosimToRenodeIndex].reset_deassert_request) begin
     bus.hresetn = 1;
     repeat (2) @(posedge clk);
-    runtime.peripheral.reset_deassert_respond();
+    runtime.peripherals[CosimToRenodeIndex].reset_deassert_respond();
   end
 
   always @(posedge clk) transaction();
@@ -46,7 +46,7 @@
 
     wait_for_transfer(address, valid_bits, direction, is_invalid);
 
-    // The runtime.peripheral.read call may consume an unknown number of clock cycles.
+    // The runtime.peripherals[CosimToRenodeIndex].read call may consume an unknown number of clock cycles.
     // To to make the logic simpler both read and write transactions contain at least one cycle with a deasserted ready.
     // It also ensures that address and data phases don't overlap between transactions.
     bus.hreadyout <= 0;
@@ -54,11 +54,11 @@
 
     if (!is_invalid) begin
       if (direction == Read) begin
-        runtime.peripheral.read(address, valid_bits, data, is_error);
+        runtime.peripherals[CosimToRenodeIndex].read(address, valid_bits, data, is_error);
         bus.hrdata = data_t'(data & valid_bits);
         if (is_error) runtime.connection.log(LogWarning, $sformatf("Unable to read data from Renode at address 'h%h", address));
       end else begin
-        runtime.peripheral.write(address, valid_bits, renode_pkg::data_t'(bus.hwdata) & valid_bits, is_error);
+        runtime.peripherals[CosimToRenodeIndex].write(address, valid_bits, renode_pkg::data_t'(bus.hwdata) & valid_bits, is_error);
         if (is_error) runtime.connection.log(LogWarning, $sformatf("Unable to write data to Renode at address 'h%h", address));
       end
     end
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/apb3/renode_apb3_completer.sv b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/apb3/renode_apb3_completer.sv
index 82411a1..595cb68 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/apb3/renode_apb3_completer.sv
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/apb3/renode_apb3_completer.sv
@@ -10,7 +10,8 @@
 import renode_pkg::renode_runtime, renode_pkg::LogWarning;
 
 module renode_apb3_completer #(
-    parameter int unsigned OutputLatency = 0
+    parameter int unsigned OutputLatency = 0,
+    int CosimToRenodeIndex = 0
 ) (
     ref renode_runtime runtime,
     renode_apb3_if bus
@@ -45,18 +46,18 @@
   assign bus.pslverr = pslverr;
 
   // Connection initiated reset
-  always @(runtime.peripheral.reset_assert_request) begin
+  always @(runtime.peripherals[CosimToRenodeIndex].reset_assert_request) begin
     rst_n = 0;
     // The reset takes 2 cycles to prevent a race condition without usage of a non-blocking assigment.
     repeat (2) @(posedge clk);
-    runtime.peripheral.reset_assert_respond();
+    runtime.peripherals[CosimToRenodeIndex].reset_assert_respond();
   end
 
-  always @(runtime.peripheral.reset_deassert_request) begin
+  always @(runtime.peripherals[CosimToRenodeIndex].reset_deassert_request) begin
     rst_n = 1;
     // There is one more wait for the clock edges to be sure that all modules aren't in a reset state.
     repeat (2) @(posedge clk);
-    runtime.peripheral.reset_deassert_respond();
+    runtime.peripherals[CosimToRenodeIndex].reset_deassert_respond();
   end
 
   renode_pkg::valid_bits_e valid_bits;
@@ -106,7 +107,7 @@
         // Workaround::Bug::Verilator::Task call inside of always block requires using fork...join
         fork
           begin
-            runtime.peripheral.write(renode_pkg::address_t'(paddr), valid_bits, renode_pkg::data_t'(pwdata),
+            runtime.peripherals[CosimToRenodeIndex].write(renode_pkg::address_t'(paddr), valid_bits, renode_pkg::data_t'(pwdata),
                              is_error);
             if (is_error) begin
               runtime.connection.log(LogWarning, "Renode connection write transfer was unable to complete");
@@ -120,8 +121,8 @@
         // Workaround::Bug::Verilator::Task call inside of always block requires using fork...join
         fork
           begin
-            // The runtime.peripheral.read call may cause elapse of a simulation time.
-            runtime.peripheral.read(renode_pkg::address_t'(paddr), valid_bits, prdata_int, is_error);
+            // The runtime.peripherals[CosimToRenodeIndex].read call may cause elapse of a simulation time.
+            runtime.peripherals[CosimToRenodeIndex].read(renode_pkg::address_t'(paddr), valid_bits, prdata_int, is_error);
             if (is_error) begin
               runtime.connection.log(LogWarning, "Renode connection read transfer was unable to complete");
             end
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/apb3/renode_apb3_requester.sv b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/apb3/renode_apb3_requester.sv
index 80fd35d..4f2e317 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/apb3/renode_apb3_requester.sv
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/apb3/renode_apb3_requester.sv
@@ -9,7 +9,7 @@
 
 import renode_pkg::renode_runtime, renode_pkg::LogWarning;
 
-module renode_apb3_requester (
+module renode_apb3_requester #(int RenodeToCosimIndex = 0) (
     ref renode_runtime runtime,
     renode_apb3_if bus
 );
@@ -54,7 +54,7 @@
   localparam int unsigned Back2BackNum = 1;
 
 
-  always @(runtime.controller.reset_assert_request) begin
+  always @(runtime.controllers[RenodeToCosimIndex].reset_assert_request) begin
     write_address = '0;
     read_address = '0;
     write_data = '0;
@@ -63,14 +63,14 @@
     rst_n = 0;
     // The reset takes 2 cycles to prevent a race condition without usage of a non-blocking assigment.
     repeat (2) @(posedge clk);
-    runtime.controller.reset_assert_respond();
+    runtime.controllers[RenodeToCosimIndex].reset_assert_respond();
   end
 
-  always @(runtime.controller.reset_deassert_request) begin
+  always @(runtime.controllers[RenodeToCosimIndex].reset_deassert_request) begin
     rst_n = 1;
     // There is one more wait for the clock edges to be sure that all modules aren't in a reset state.
     repeat (2) @(posedge clk);
-    runtime.controller.reset_deassert_respond();
+    runtime.controllers[RenodeToCosimIndex].reset_deassert_respond();
   end
 
   // Internal state
@@ -85,13 +85,13 @@
   // Waveform generation
   //
 
-  always @(runtime.controller.read_transaction_request) begin
+  always @(runtime.controllers[RenodeToCosimIndex].read_transaction_request) begin
     integer transaction_width;
 
-    if(!renode_pkg::is_access_aligned(runtime.controller.read_transaction_address, runtime.controller.read_transaction_data_bits)) begin
+    if(!renode_pkg::is_access_aligned(runtime.controllers[RenodeToCosimIndex].read_transaction_address, runtime.controllers[RenodeToCosimIndex].read_transaction_data_bits)) begin
         runtime.connection.log(LogWarning, "Unaligned access on APB bus results in unpredictable behavior");
     end
-    transaction_width = renode_pkg::valid_bits_to_transaction_width(runtime.controller.read_transaction_data_bits);
+    transaction_width = renode_pkg::valid_bits_to_transaction_width(runtime.controllers[RenodeToCosimIndex].read_transaction_data_bits);
     if (bus.DataWidth > transaction_width) begin
       runtime.connection.log(LogWarning,
           $sformatf("Bus bus.bus.DataWidth is (%d) > transaction width (%d), MSB will be truncated.", bus.DataWidth, transaction_width));
@@ -100,19 +100,19 @@
           $sformatf("Bus bus.bus.DataWidth is (%d) < transaction width (%d), MSB will be zero-extended.", bus.DataWidth, transaction_width));
     end
 
-    read_address = address_t'(runtime.controller.read_transaction_address);
+    read_address = address_t'(runtime.controllers[RenodeToCosimIndex].read_transaction_address);
     write_mode = 1'b0;
     start_transaction = 1'b1;
     @(posedge clk) start_transaction <= 1'b0;
   end
 
-  always @(runtime.controller.write_transaction_request) begin
+  always @(runtime.controllers[RenodeToCosimIndex].write_transaction_request) begin
     integer transaction_width;
 
-    if(!renode_pkg::is_access_aligned(runtime.controller.write_transaction_address, runtime.controller.write_transaction_data_bits)) begin
+    if(!renode_pkg::is_access_aligned(runtime.controllers[RenodeToCosimIndex].write_transaction_address, runtime.controllers[RenodeToCosimIndex].write_transaction_data_bits)) begin
         runtime.connection.log(LogWarning, "Unaligned access on APB bus results in unpredictable behavior");
     end
-    transaction_width = renode_pkg::valid_bits_to_transaction_width(runtime.controller.write_transaction_data_bits);
+    transaction_width = renode_pkg::valid_bits_to_transaction_width(runtime.controllers[RenodeToCosimIndex].write_transaction_data_bits);
     if (bus.DataWidth > transaction_width) begin
       runtime.connection.log(LogWarning,
           $sformatf("Bus bus.bus.DataWidth is (%d) > transaction width (%d), MSB will be truncated.", bus.DataWidth, transaction_width));
@@ -121,8 +121,8 @@
           $sformatf("Bus bus.bus.DataWidth is (%d) < transaction width (%d), MSB will be zero-extended.", bus.DataWidth, transaction_width));
     end
 
-    write_address = address_t'(runtime.controller.write_transaction_address);
-    write_data = data_t'(runtime.controller.write_transaction_data);
+    write_address = address_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_address);
+    write_data = data_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_data);
     write_mode = 1'b1;
     start_transaction = 1'b1;
     @(posedge clk) start_transaction <= 1'b0;
@@ -174,9 +174,9 @@
         S_ACCESS: begin
           if (pready) begin
             if (write_mode) begin
-              runtime.controller.write_respond(1'b0);  // Notify Renode that write is done
+              runtime.controllers[RenodeToCosimIndex].write_respond(1'b0);  // Notify Renode that write is done
             end else begin
-              runtime.controller.read_respond(renode_pkg::data_t'(prdata), 1'b0);
+              runtime.controllers[RenodeToCosimIndex].read_respond(renode_pkg::data_t'(prdata), 1'b0);
             end
           end
         end
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/axi/renode_axi_manager.sv b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/axi/renode_axi_manager.sv
index 5d70427..a523c36 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/axi/renode_axi_manager.sv
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/axi/renode_axi_manager.sv
@@ -9,7 +9,7 @@
 
 import renode_pkg::renode_runtime, renode_pkg::LogWarning;
 
-module renode_axi_manager (
+module renode_axi_manager #(int RenodeToCosimIndex = 0) (
     ref renode_runtime runtime,
     renode_axi_if bus
 );
@@ -22,25 +22,25 @@
 
   wire clk = bus.aclk;
 
-  always @(runtime.controller.reset_assert_request) begin
+  always @(runtime.controllers[RenodeToCosimIndex].reset_assert_request) begin
     bus.arvalid = 0;
     bus.awvalid = 0;
     bus.wvalid  = 0;
     bus.areset_n = 0;
     // The reset takes 2 cycles to prevent a race condition without usage of a non-blocking assigment.
     repeat (2) @(posedge clk);
-    runtime.controller.reset_assert_respond();
+    runtime.controllers[RenodeToCosimIndex].reset_assert_respond();
   end
 
-  always @(runtime.controller.reset_deassert_request) begin
+  always @(runtime.controllers[RenodeToCosimIndex].reset_deassert_request) begin
     bus.areset_n = 1;
     // There is one more wait for the clock edges to be sure that all modules aren't in a reset state.
     repeat (2) @(posedge clk);
-    runtime.controller.reset_deassert_respond();
+    runtime.controllers[RenodeToCosimIndex].reset_deassert_respond();
   end
 
-  always @(runtime.controller.read_transaction_request) read_transaction();
-  always @(runtime.controller.write_transaction_request) write_transaction();
+  always @(runtime.controllers[RenodeToCosimIndex].read_transaction_request) read_transaction();
+  always @(runtime.controllers[RenodeToCosimIndex].write_transaction_request) write_transaction();
 
   task static read_transaction();
     bit is_error;
@@ -49,18 +49,18 @@
     burst_size_t burst_size;
     data_t data;
 
-    address = address_t'(runtime.controller.read_transaction_address);
-    valid_bits = runtime.controller.read_transaction_data_bits;
+    address = address_t'(runtime.controllers[RenodeToCosimIndex].read_transaction_address);
+    valid_bits = runtime.controllers[RenodeToCosimIndex].read_transaction_data_bits;
 
     if(!is_access_valid(address, valid_bits)) begin
-      runtime.controller.read_respond(0, 1);
+      runtime.controllers[RenodeToCosimIndex].read_respond(0, 1);
     end else begin
       burst_size = bus.valid_bits_to_burst_size(valid_bits);
 
       read(0, address, burst_size, data, is_error);
 
       data = data >> ((address % bus.StrobeWidth) * 8);
-      runtime.controller.read_respond(renode_pkg::data_t'(data) & valid_bits, is_error);
+      runtime.controllers[RenodeToCosimIndex].read_respond(renode_pkg::data_t'(data) & valid_bits, is_error);
     end
   endtask
 
@@ -72,20 +72,20 @@
     data_t data;
     strobe_t strobe;
 
-    address = address_t'(runtime.controller.write_transaction_address);
-    valid_bits = runtime.controller.write_transaction_data_bits;
+    address = address_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_address);
+    valid_bits = runtime.controllers[RenodeToCosimIndex].write_transaction_data_bits;
 
     if(!is_access_valid(address, valid_bits)) begin
-      runtime.controller.write_respond(1);
+      runtime.controllers[RenodeToCosimIndex].write_respond(1);
     end else begin
       burst_size = bus.valid_bits_to_burst_size(valid_bits);
-      data = data_t'(runtime.controller.write_transaction_data & valid_bits);
+      data = data_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_data & valid_bits);
       strobe = bus.burst_size_to_strobe(burst_size) << (address % bus.StrobeWidth);
       data = data << ((address % bus.StrobeWidth) * 8);
 
       write(0, address, burst_size, strobe, data, is_error);
 
-      runtime.controller.write_respond(is_error);
+      runtime.controllers[RenodeToCosimIndex].write_respond(is_error);
     end
   endtask
 
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/axi/renode_axi_subordinate.sv b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/axi/renode_axi_subordinate.sv
index b68cdb5..3b9578d 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/axi/renode_axi_subordinate.sv
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/axi/renode_axi_subordinate.sv
@@ -9,7 +9,7 @@
 
 import renode_pkg::renode_runtime, renode_pkg::LogWarning;
 
-module renode_axi_subordinate (
+module renode_axi_subordinate #(int CosimToRenodeIndex = 0) (
     ref renode_runtime runtime,
     renode_axi_if bus
 );
@@ -22,20 +22,20 @@
 
   wire clk = bus.aclk;
 
-  always @(runtime.peripheral.reset_assert_request) begin
+  always @(runtime.peripherals[CosimToRenodeIndex].reset_assert_request) begin
     bus.rvalid = 0;
     bus.bvalid = 0;
     bus.areset_n = 0;
     // The reset takes 2 cycles to prevent a race condition without usage of a non-blocking assigment.
     repeat (2) @(posedge clk);
-    runtime.peripheral.reset_assert_respond();
+    runtime.peripherals[CosimToRenodeIndex].reset_assert_respond();
   end
 
-  always @(runtime.peripheral.reset_deassert_request) begin
+  always @(runtime.peripherals[CosimToRenodeIndex].reset_deassert_request) begin
     bus.areset_n = 1;
     // There is one more wait for the clock edges to be sure that all modules aren't in a reset state.
     repeat (2) @(posedge clk);
-    runtime.peripheral.reset_deassert_respond();
+    runtime.peripherals[CosimToRenodeIndex].reset_deassert_respond();
   end
 
   always @(clk) read_transaction();
@@ -63,7 +63,7 @@
       end
       else begin
           // The conection.read call may cause simulation time to move forward
-          runtime.peripheral.read(renode_pkg::address_t'(address), valid_bits, data, is_error);
+          runtime.peripherals[CosimToRenodeIndex].read(renode_pkg::address_t'(address), valid_bits, data, is_error);
           if (is_error) begin
             runtime.connection.log(LogWarning, $sformatf("Unable to read data from Renode at address 'h%h, responding with 0.", address));
             data = 0;
@@ -111,7 +111,7 @@
 
         if (bus.wlast != (address == address_last)) runtime.connection.log(LogWarning, "Unexpected state of the wlast signal.");
         // The conection.write call may cause simulation time to move forward
-        runtime.peripheral.write(renode_pkg::address_t'(address), valid_bits, renode_pkg::data_t'(data) & valid_bits, is_error);
+        runtime.peripherals[CosimToRenodeIndex].write(renode_pkg::address_t'(address), valid_bits, renode_pkg::data_t'(data) & valid_bits, is_error);
         if (is_error) runtime.connection.log(LogWarning, $sformatf("Unable to write data to Renode at address 'h%h", address));
       end
 
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/renode.sv b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/renode.sv
index 7605c66..26fb41f 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/renode.sv
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/renode.sv
@@ -7,12 +7,12 @@
 
 `timescale 1ns / 1ps
 
-import renode_pkg::renode_runtime;
+import renode_pkg::renode_runtime, renode_pkg::bus_connection, renode_pkg::renode_connection, renode_pkg::no_peripheral_index;
 import renode_pkg::message_t, renode_pkg::address_t, renode_pkg::data_t, renode_pkg::valid_bits_e;
 
 module renode #(
-    int unsigned BusControllersCount = 0,
-    int unsigned BusPeripheralsCount = 0,
+    int unsigned RenodeToCosimCount = 0,
+    int unsigned CosimToRenodeCount = 0,
     int unsigned RenodeInputsCount = 1,
     int unsigned RenodeOutputsCount = 1
 ) (
@@ -23,6 +23,14 @@
 );
   time renode_time = 0;
 
+  event reset_assert_all;
+  int reset_assert_done_count;
+  event reset_assert_done;
+
+  event reset_deassert_all;
+  int reset_deassert_done_count;
+  event reset_deassert_done;
+
   renode_inputs #(
       .InputsCount(RenodeInputsCount)
   ) gpio (
@@ -31,8 +39,59 @@
       .inputs(renode_inputs)
   );
 
-  always @(runtime.peripheral.read_transaction_request) read_transaction();
-  always @(runtime.peripheral.write_transaction_request) write_transaction();
+  initial begin
+    runtime.controllers = new[RenodeToCosimCount];
+    foreach(runtime.controllers[i]) begin
+      runtime.controllers[i] = new();
+    end
+
+    runtime.peripherals = new[CosimToRenodeCount];
+    foreach(runtime.peripherals[i]) begin
+      runtime.peripherals[i] = new();
+    end
+  end
+
+  if(CosimToRenodeCount > 0) begin
+    genvar i;
+    for(i = 0; i < CosimToRenodeCount; i += 1) begin
+      always @(runtime.peripherals[i].read_transaction_request) read_transaction(i);
+      always @(runtime.peripherals[i].write_transaction_request) write_transaction(i);
+      always @(reset_assert_all) runtime.peripherals[i].reset_assert();
+      always @(runtime.peripherals[i].reset_assert_response) begin
+          reset_assert_done_count++;
+          if(reset_assert_done_count == (CosimToRenodeCount + RenodeToCosimCount)) begin
+              ->reset_assert_done;
+          end
+      end
+      always @(runtime.peripherals[i].reset_deassert_response) begin
+          reset_deassert_done_count++;
+          if(reset_deassert_done_count == (CosimToRenodeCount + RenodeToCosimCount)) begin
+              ->reset_deassert_done;
+          end
+      end
+      always @(reset_deassert_all) runtime.peripherals[i].reset_deassert();
+    end
+  end
+
+  if(RenodeToCosimCount > 0) begin
+    genvar i;
+    for(i = 0; i < RenodeToCosimCount; i += 1) begin
+      always @(reset_assert_all) runtime.controllers[i].reset_assert();
+      always @(reset_deassert_all) runtime.controllers[i].reset_deassert();
+      always @(runtime.controllers[i].reset_assert_response) begin
+          reset_assert_done_count++;
+          if(reset_assert_done_count == (CosimToRenodeCount + RenodeToCosimCount)) begin
+              ->reset_assert_done;
+          end
+      end
+      always @(runtime.controllers[i].reset_deassert_response) begin
+          reset_deassert_done_count++;
+          if(reset_deassert_done_count == (CosimToRenodeCount + RenodeToCosimCount)) begin
+              ->reset_deassert_done;
+          end
+      end
+    end
+  end
 
   task static receive_and_handle_message();
     message_t message;
@@ -54,14 +113,14 @@
       renode_pkg::resetPeripheral: reset();
       renode_pkg::tickClock: sync_time(time'(message.data));
       renode_pkg::interrupt: handle_renode_output(message.address, message.data[0]);
-      renode_pkg::writeRequestQuadWord: write_to_bus(message.address, renode_pkg::QuadWord, message.data);
-      renode_pkg::writeRequestDoubleWord: write_to_bus(message.address, renode_pkg::DoubleWord, message.data);
-      renode_pkg::writeRequestWord: write_to_bus(message.address, renode_pkg::Word, message.data);
-      renode_pkg::writeRequestByte: write_to_bus(message.address, renode_pkg::Byte, message.data);
-      renode_pkg::readRequestQuadWord: read_from_bus(message.address, renode_pkg::QuadWord);
-      renode_pkg::readRequestDoubleWord: read_from_bus(message.address, renode_pkg::DoubleWord);
-      renode_pkg::readRequestWord: read_from_bus(message.address, renode_pkg::Word);
-      renode_pkg::readRequestByte: read_from_bus(message.address, renode_pkg::Byte);
+      renode_pkg::writeRequestQuadWord: write_to_bus(message.address, renode_pkg::QuadWord, message.data, message.peripheral_index);
+      renode_pkg::writeRequestDoubleWord: write_to_bus(message.address, renode_pkg::DoubleWord, message.data, message.peripheral_index);
+      renode_pkg::writeRequestWord: write_to_bus(message.address, renode_pkg::Word, message.data, message.peripheral_index);
+      renode_pkg::writeRequestByte: write_to_bus(message.address, renode_pkg::Byte, message.data, message.peripheral_index);
+      renode_pkg::readRequestQuadWord: read_from_bus(message.address, renode_pkg::QuadWord, message.peripheral_index);
+      renode_pkg::readRequestDoubleWord: read_from_bus(message.address, renode_pkg::DoubleWord, message.peripheral_index);
+      renode_pkg::readRequestWord: read_from_bus(message.address, renode_pkg::Word, message.peripheral_index);
+      renode_pkg::readRequestByte: read_from_bus(message.address, renode_pkg::Byte, message.peripheral_index);
       default: is_handled = 0;
     endcase
 
@@ -70,41 +129,33 @@
   endtask
 
   task static reset();
+    // Nothing to reset, return immediately.
+    if(RenodeToCosimCount + CosimToRenodeCount == 0) return;
+
     // The reset just locks the connection without using it to avoid an unexpected behaviour.
     // It also prevents from a message handling in the receive_and_handle_message until a reset deassertion.
     runtime.connection.exclusive_receive.get();
 
-    // The delay was added to avoid a race condition.
-    // It may occure when an event is triggered before executing a wait for this event.
-    // A non-blocking trigger, which is an alternative solution, isn't supported by Verilator.
+    reset_assert_done_count = 0;
     #1 fork
-      begin
-        if (BusPeripheralsCount > 0)
-          runtime.peripheral.reset_assert();
-      end
-      begin
-        if (BusControllersCount > 0)
-          runtime.controller.reset_assert();
-      end
-      gpio.reset_assert();
+        ->reset_assert_all;
+        gpio.reset_assert();
     join
 
+    @(reset_assert_done);
+
     // It's required to make values of all signals known (different than `x`) before a deassertion of resets.
     // The assignment to renode_outputs is an equivalent of a reset assertion.
     renode_outputs = 0;
 
+    reset_deassert_done_count = 0;
     fork
-      begin
-        if (BusPeripheralsCount > 0)
-          runtime.peripheral.reset_deassert();
-      end
-      begin
-        if (BusControllersCount > 0)
-          runtime.controller.reset_deassert();
-      end
+      ->reset_deassert_all;
       gpio.reset_deassert();
     join
 
+    @(reset_deassert_done);
+
     runtime.connection.exclusive_receive.put();
   endtask
 
@@ -112,87 +163,97 @@
     renode_time = renode_time + time_to_elapse;
     while ($time < renode_time) @(clk);
 
-    runtime.connection.send_to_async_receiver(message_t'{renode_pkg::tickClock, 0, 0});
+    runtime.connection.send_to_async_receiver(message_t'{renode_pkg::tickClock, 0, 0, renode_pkg::no_peripheral_index});
     runtime.connection.log(renode_pkg::LogNoisy, $sformatf("Simulation time synced to %t", $realtime));
   endtask
 
-  task automatic read_from_bus(address_t address, valid_bits_e data_bits);
+  task automatic read_from_bus(address_t address, valid_bits_e data_bits, int peripheral_index);
     data_t data = 0;
     bit is_error = 0;
-    runtime.controller.read(address, data_bits, data, is_error);
+    runtime.controllers[peripheral_index].read(address, data_bits, data, is_error);
 
-    if (is_error) runtime.connection.send(message_t'{renode_pkg::error, 0, 0});
-    else runtime.connection.send(message_t'{renode_pkg::readRequest, address, data});
+    if (is_error) runtime.connection.send(message_t'{renode_pkg::error, 0, 0, peripheral_index});
+    else runtime.connection.send(message_t'{renode_pkg::readRequest, address, data, peripheral_index});
   endtask
 
-  task automatic write_to_bus(address_t address, valid_bits_e data_bits, data_t data);
+  task automatic write_to_bus(address_t address, valid_bits_e data_bits, data_t data, int peripheral_index);
     bit is_error = 0;
-    runtime.controller.write(address, data_bits, data, is_error);
+    runtime.controllers[peripheral_index].write(address, data_bits, data, is_error);
 
-    if (is_error) runtime.connection.send(message_t'{renode_pkg::error, 0, 0});
-    else runtime.connection.send(message_t'{renode_pkg::ok, 0, 0});
+    if (is_error) runtime.connection.send(message_t'{renode_pkg::error, 0, 0, peripheral_index});
+    else runtime.connection.send(message_t'{renode_pkg::ok, 0, 0, peripheral_index});
   endtask
 
-  task static read_transaction();
+  task automatic read_transaction(int peripheral_index);
     message_t message;
 
-    case (runtime.peripheral.read_transaction_data_bits)
+    case (runtime.peripherals[peripheral_index].read_transaction_data_bits)
       renode_pkg::Byte: message.action = renode_pkg::getByte;
       renode_pkg::Word: message.action = renode_pkg::getWord;
       renode_pkg::DoubleWord: message.action = renode_pkg::getDoubleWord;
       renode_pkg::QuadWord: message.action = renode_pkg::getQuadWord;
       default: begin
-        runtime.connection.fatal_error($sformatf("Renode doesn't support access with the 'b%b mask from a bus controller.", runtime.peripheral.read_transaction_data_bits));
-        runtime.peripheral.read_respond(0, 1);
+        runtime.connection.fatal_error($sformatf("Renode doesn't support access with the 'b%b mask from a bus controller.", runtime.peripherals[peripheral_index].read_transaction_data_bits));
+        runtime.peripherals[peripheral_index].read_respond(0, 1);
         return;
       end
     endcase
-    message.address = runtime.peripheral.read_transaction_address;
+    message.address = runtime.peripherals[peripheral_index].read_transaction_address;
     message.data = 0;
 
     runtime.connection.exclusive_receive.get();
+    if(!runtime.connection.is_connected()) begin
+        runtime.connection.exclusive_receive.put();
+        return;
+    end
 
     runtime.connection.send_to_async_receiver(message);
 
     runtime.connection.receive(message);
     while (message.action != renode_pkg::writeRequest) begin
       handle_message(message);
+      if(message.action == renode_pkg::disconnect) break;
       runtime.connection.receive(message);
     end
 
     runtime.connection.exclusive_receive.put();
-    runtime.peripheral.read_respond(message.data, 0);
+    runtime.peripherals[peripheral_index].read_respond(message.data, 0);
   endtask
 
-  task static write_transaction();
+  task automatic write_transaction(int peripheral_index);
     message_t message;
 
-    case (runtime.peripheral.write_transaction_data_bits)
+    case (runtime.peripherals[peripheral_index].write_transaction_data_bits)
       renode_pkg::Byte: message.action = renode_pkg::pushByte;
       renode_pkg::Word: message.action = renode_pkg::pushWord;
       renode_pkg::DoubleWord: message.action = renode_pkg::pushDoubleWord;
       renode_pkg::QuadWord: message.action = renode_pkg::pushQuadWord;
       default: begin
-        runtime.connection.fatal_error($sformatf("Renode doesn't support access with the 'b%b mask from a bus controller.", runtime.peripheral.read_transaction_data_bits));
-        runtime.peripheral.write_respond(1);
+        runtime.connection.fatal_error($sformatf("Renode doesn't support access with the 'b%b mask from a bus controller.", runtime.peripherals[peripheral_index].read_transaction_data_bits));
+        runtime.peripherals[peripheral_index].write_respond(1);
         return;
       end
     endcase
-    message.address = runtime.peripheral.write_transaction_address;
-    message.data = runtime.peripheral.write_transaction_data;
+    message.address = runtime.peripherals[peripheral_index].write_transaction_address;
+    message.data = runtime.peripherals[peripheral_index].write_transaction_data;
 
     runtime.connection.exclusive_receive.get();
+    if(!runtime.connection.is_connected()) begin
+        runtime.connection.exclusive_receive.put();
+        return;
+    end
 
     runtime.connection.send_to_async_receiver(message);
     runtime.connection.receive(message);
     while (message.action != renode_pkg::pushConfirmation) begin
       handle_message(message);
+      if(message.action == renode_pkg::disconnect) break;
       runtime.connection.receive(message);
     end
 
     runtime.connection.exclusive_receive.put();
 
-    runtime.peripheral.write_respond(0);
+    runtime.peripherals[peripheral_index].write_respond(0);
   endtask
 
   // calculate number of bits needed to hold the output number
@@ -202,12 +263,12 @@
   task automatic handle_renode_output(address_t number, bit value);
     if (number >= 64'(RenodeOutputsCount)) begin
       runtime.connection.log(renode_pkg::LogWarning, $sformatf("Output %0d is out of range of [0;%0d]", number, RenodeOutputsCount - 1));
-      runtime.connection.send(message_t'{renode_pkg::error, 0, 0});
+      runtime.connection.send(message_t'{renode_pkg::error, 0, 0, renode_pkg::no_peripheral_index});
     end
 
     @(posedge clk);
     renode_outputs[number[RenodeOutputsCountWidth-1:0]] <= value;
 
-    runtime.connection.send(message_t'{renode_pkg::ok, 0, 0});
+    runtime.connection.send(message_t'{renode_pkg::ok, 0, 0, renode_pkg::no_peripheral_index});
   endtask
 endmodule
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/renode_inputs.sv b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/renode_inputs.sv
index 7548138..9581602 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/renode_inputs.sv
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/modules/renode_inputs.sv
@@ -7,7 +7,7 @@
 
 `timescale 1ns / 1ps
 
-import renode_pkg::renode_runtime;
+import renode_pkg::renode_runtime, renode_pkg::no_peripheral_index;
 
 module renode_inputs #(
     int unsigned InputsCount = 1
@@ -36,7 +36,8 @@
           runtime.connection.send_to_async_receiver(renode_pkg::message_t'{
               renode_pkg::interrupt,
               renode_pkg::address_t'(addr),
-              renode_pkg::data_t'(inputs[addr])
+              renode_pkg::data_t'(inputs[addr]),
+              renode_pkg::no_peripheral_index
             });
         end
       end
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/renode_pkg.sv b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/renode_pkg.sv
index 5529c31..98c73d8 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/renode_pkg.sv
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/hdl/renode_pkg.sv
@@ -17,10 +17,13 @@
     `include "renode_action_enumerators.svh"
   } action_e;
 
+  const int no_peripheral_index = -1;
+
   typedef struct {
     action_e action;
     address_t address;
     data_t data;
+    int peripheral_index;
   } message_t;
 
   typedef enum data_t {
@@ -56,19 +59,22 @@
   import "DPI-C" function bit renodeDPIReceive(
     output action_e action,
     output address_t address,
-    output data_t data
+    output data_t data,
+    output int peripheralIndex 
   );
 
   import "DPI-C" function bit renodeDPISend(
     action_e action,
     address_t address,
-    data_t data
+    data_t data,
+    int peripheralIndex
   );
 
   import "DPI-C" function bit renodeDPISendToAsync(
     action_e action,
     address_t address,
-    data_t data
+    data_t data,
+    int peripheralIndex
   );
 
   function static bit is_access_aligned(address_t address, valid_bits_e valid_bits);
@@ -146,10 +152,10 @@
     endfunction
 
     function bit try_receive(output message_t message);
-      bit is_received = renodeDPIReceive(message.action, message.address, message.data);
+      bit is_received = renodeDPIReceive(message.action, message.address, message.data, message.peripheral_index);
       if(is_received) begin
 `ifdef RENODE_DEBUG
-      $display("Renode at %t: Received action %0s, address = 'h%h, data = 'h%h", $realtime, message.action.name(), message.address, message.data);
+      $display("Renode at %t: Received action %0s, address = 'h%h, data = 'h%h, peripheral index: %d", $realtime, message.action.name(), message.address, message.data, message.peripheral_index);
 `endif
       end
       return is_received;
@@ -157,16 +163,16 @@
 
     function void send(message_t message);
 `ifdef RENODE_DEBUG
-      $display("Renode at %t: Sent action %0s, address = 'h%h, data = 'h%h", $realtime, message.action.name(), message.address, message.data);
+      $display("Renode at %t: Sent action %0s, address = 'h%h, data = 'h%h, peripheral index: %d", $realtime, message.action.name(), message.address, message.data, message.peripheral_index);
 `endif
-      if (!renodeDPISend(message.action, message.address, message.data)) fatal_error("Unexpected channel disconnection");
+      if (!renodeDPISend(message.action, message.address, message.data, message.peripheral_index)) fatal_error("Unexpected channel disconnection");
     endfunction
 
     function void send_to_async_receiver(message_t message);
 `ifdef RENODE_DEBUG
-      $display("Renode at %t: Sent async action %0s, address = 'h%h, data = 'h%h", $realtime, message.action.name(), message.address, message.data);
+      $display("Renode at %t: Sent async action %0s, address = 'h%h, data = 'h%h, peripheral index: %d", $realtime, message.action.name(), message.address, message.data, message.peripheral_index);
 `endif
-      if (!renodeDPISendToAsync(message.action, message.address, message.data)) fatal_error("Unexpected channel disconnection");
+      if (!renodeDPISendToAsync(message.action, message.address, message.data, message.peripheral_index)) fatal_error("Unexpected channel disconnection");
     endfunction
 
     local function void disconnect();
@@ -174,7 +180,7 @@
     endfunction
 
     local function void handle_disconnect();
-      send(message_t'{ok, 0, 0});
+      send(message_t'{ok, 0, 0, renode_pkg::no_peripheral_index});
       disconnect();
 `ifdef RENODE_DEBUG
       $display("Renode at %t: disconnected", $realtime);
@@ -262,8 +268,10 @@
     const string AddressArgName = "RENODE_ADDRESS";
 
     renode_connection connection = new();
-    bus_connection controller = new();
-    bus_connection peripheral = new();
+
+    // Initialized by the Renode module
+    bus_connection controllers[];
+    bus_connection peripherals[];
 
     function void connect_plus_args();
       int receiver_port, sender_port;
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/communication/socket_channel.cpp b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/communication/socket_channel.cpp
index 64cfcd1..c287630 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/communication/socket_channel.cpp
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/communication/socket_channel.cpp
@@ -35,14 +35,14 @@
 {
     Protocol* received = receive();
     if(received->actionId == handshake) {
-        sendMain(Protocol(handshake, 0, 0));
+        sendMain(Protocol(handshake, 0, 0, noPeripheralIndex));
         isConnected = true;
     }
 }
 
 void SocketCommunicationChannel::log(int logLevel, const char* data)
 {
-    sendSender(Protocol(logMessage, strlen(data), logLevel));
+    sendSender(Protocol(logMessage, strlen(data), logLevel, noPeripheralIndex));
     senderSocket->Send(data, strlen(data));
 }
 
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/renode.h b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/renode.h
index 07fd0cb..c68eef7 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/renode.h
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/renode.h
@@ -15,16 +15,18 @@
 struct Protocol
 {
   Protocol() = default;
-  Protocol(int actionId, uint64_t addr, uint64_t value)
+  Protocol(int actionId, uint64_t addr, uint64_t value, int peripheralIndex = 0)
   {
     this->actionId = actionId;
     this->addr = addr;
     this->value = value;
+    this->peripheralIndex = peripheralIndex;
   }
 
   int actionId;
   uint64_t addr;
   uint64_t value;
+  int peripheralIndex;
 };
 #pragma pack(pop)
 
@@ -42,4 +44,6 @@
   LOG_LEVEL_ERROR   = 3
 };
 
+const int noPeripheralIndex = -1;
+
 #endif
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/renode_dpi.cpp b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/renode_dpi.cpp
index 0fdac9f..c5718a0 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/renode_dpi.cpp
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/renode_dpi.cpp
@@ -10,7 +10,7 @@
 
 static SocketCommunicationChannel *socketChannel;
 
-bool renodeDPIReceive(uint32_t* actionId, uint64_t* address, uint64_t* value)
+bool renodeDPIReceive(uint32_t* actionId, uint64_t* address, uint64_t* value, int32_t* peripheralIndex)
 {
     if(!socketChannel->getIsConnected())
     {
@@ -20,6 +20,8 @@
     *actionId = message->actionId;
     *address = message->addr;
     *value = message->value;
+    *peripheralIndex = message->peripheralIndex;
+
     delete message;
     return true;
 }
@@ -43,23 +45,23 @@
     return socketChannel != NULL && socketChannel->getIsConnected();
 }
 
-bool renodeDPISend(uint32_t actionId, uint64_t address, uint64_t value)
+bool renodeDPISend(uint32_t actionId, uint64_t address, uint64_t value, int32_t peripheralIndex)
 {
     if(socketChannel == NULL || !socketChannel->getIsConnected())
     {
         return false;
     }
-    socketChannel->sendMain(Protocol(actionId, address, value));
+    socketChannel->sendMain(Protocol(actionId, address, value, peripheralIndex));
     return true;
 }
 
-bool renodeDPISendToAsync(uint32_t actionId, uint64_t address, uint64_t value)
+bool renodeDPISendToAsync(uint32_t actionId, uint64_t address, uint64_t value, int32_t peripheralIndex)
 {
     if(socketChannel == NULL || !socketChannel->getIsConnected())
     {
         return false;
     }
-    socketChannel->sendSender(Protocol(actionId, address, value));
+    socketChannel->sendSender(Protocol(actionId, address, value, peripheralIndex));
     return true;
 }
 
diff --git a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/renode_dpi.h b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/renode_dpi.h
index 5b3a959..8bccf9f 100644
--- a/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/renode_dpi.h
+++ b/src/Plugins/CoSimulationPlugin/IntegrationLibrary/src/renode_dpi.h
@@ -13,9 +13,9 @@
   void renodeDPIConnect(int receiverPort, int senderPort, const char *address);
   void renodeDPIDisconnect();
   bool renodeDPIIsConnected();
-  bool renodeDPIReceive(uint32_t *actionId, uint64_t *address, uint64_t *value);
-  bool renodeDPISend(uint32_t actionId, uint64_t address, uint64_t value);
-  bool renodeDPISendToAsync(uint32_t actionId, uint64_t address, uint64_t value);
+  bool renodeDPIReceive(uint32_t *actionId, uint64_t *address, uint64_t *value, int32_t *peripheralIndex);
+  bool renodeDPISend(uint32_t actionId, uint64_t address, uint64_t value, int32_t peripheralIndex);
+  bool renodeDPISendToAsync(uint32_t actionId, uint64_t address, uint64_t value, int32_t peripheralIndex);
   bool renodeDPILog(int logLevel, const char *data);
 }