[#69049] Add SystemC cosimulation tests
diff --git a/tests/platforms/systemc/dma/dma.robot b/tests/platforms/systemc/dma/dma.robot
new file mode 100644
index 0000000..5313ab1
--- /dev/null
+++ b/tests/platforms/systemc/dma/dma.robot
@@ -0,0 +1,65 @@
+*** Variables ***
+${SYSTEMC_BINARY}                    @https://dl.antmicro.com/projects/renode/x64-systemc--dma.elf-s_943520-d936f6165ff706163a1d722c301a11a55672c365
+${PLATFORM}=    SEPARATOR=
+...  """                                                                        ${\n}
+...  memory: Memory.MappedMemory @ sysbus 0x20000000                            ${\n}
+...  ${SPACE*4}size: 0x1000000                                                  ${\n}
+...                                                                             ${\n}
+...  dma_systemc: SystemC.SystemCPeripheral @ sysbus <0x9000000, +0xffffff>     ${\n}
+...  ${SPACE*4}address: "127.0.0.1"                                             ${\n}
+...  ${SPACE*4}timeSyncPeriodUS: 5000                                           ${\n}
+...  """
+
+*** Keywords ***
+Create Machine
+    Execute Command                 mach create
+    Execute Command                 machine LoadPlatformDescriptionFromString ${PLATFORM}
+    Execute Command                 sysbus.dma_systemc SystemCExecutablePath ${SYSTEMC_BINARY}
+
+Memory Should Be Equal To
+    [Arguments]                     ${address}  ${value}
+    ${res}=                         Execute Command  sysbus ReadByte ${address}
+    Should Be Equal As Numbers      ${res}  ${value}
+
+*** Test Cases ***
+Should Perform Memory-To-Memory Transfer
+    [Tags]                          skip_windows    skip_osx
+    Create Machine
+    Start Emulation
+
+    # Initialize memory
+    Execute Command                 sysbus.memory WriteString 0 "Hello"
+
+    # Make sure memory destination is initialized to 0
+    Memory Should Be Equal To       0x20000010  0
+    Memory Should Be Equal To       0x20000011  0
+    Memory Should Be Equal To       0x20000012  0
+    Memory Should Be Equal To       0x20000013  0
+    Memory Should Be Equal To       0x20000014  0
+
+    # Set source address register in dma_systemc.
+    Execute Command                 sysbus WriteDoubleWord 0x9000004 0x20000000
+
+    # Set destination address register in dma_systemc.
+    Execute Command                 sysbus WriteDoubleWord 0x9000008 0x20000010
+
+    # Set data length register (in bytes) in dma_systemc.
+    Execute Command                 sysbus WriteDoubleWord 0x900000C 5
+
+    # Start memory-to-memory transfer in dma_systemc
+    Execute Command                 sysbus WriteDoubleWord 0x9000010 0x1
+
+    # Signal "bus free" to notify DMAC it can use the bus now.
+    Execute Command                 sysbus.dma_systemc OnGPIO 2 True
+
+    # Verify the memory was copied correctly.
+    # "H"
+    Memory Should Be Equal To       0x20000010  0x48
+    # "e"
+    Memory Should Be Equal To       0x20000011  0x65
+    # "l"
+    Memory Should Be Equal To       0x20000012  0x6C
+    # "l"
+    Memory Should Be Equal To       0x20000013  0x6C
+    # "o"
+    Memory Should Be Equal To       0x20000014  0x6F
diff --git a/tests/platforms/systemc/interrupts/interrupts.repl b/tests/platforms/systemc/interrupts/interrupts.repl
new file mode 100644
index 0000000..734cdbe
--- /dev/null
+++ b/tests/platforms/systemc/interrupts/interrupts.repl
@@ -0,0 +1,79 @@
+flash0: Memory.MappedMemory @ sysbus 0x8000000
+    size: 0x40000
+
+sram0: Memory.MappedMemory @ sysbus 0x20000000
+    size: 0x10000
+
+timers4: Timers.STM32_Timer @ sysbus <0x40000800, +0x400>
+    frequency: 10000000
+    initialLimit: 0xffffffff
+    ->nvic0@30
+
+usart1: UART.STM32_UART @ sysbus <0x40011000, +0x400>
+    ->nvic0@37
+
+gpioa: GPIOPort.STM32_GPIOPort @ sysbus <0x40020000, +0x400>
+
+gpiob: GPIOPort.STM32_GPIOPort @ sysbus <0x40020400, +0x400>
+
+gpioc: GPIOPort.STM32_GPIOPort @ sysbus <0x40020800, +0x400>
+
+gpiod: GPIOPort.STM32_GPIOPort @ sysbus <0x40020c00, +0x400>
+
+gpioe: GPIOPort.STM32_GPIOPort @ sysbus <0x40021000, +0x400>
+
+gpiof: GPIOPort.STM32_GPIOPort @ sysbus <0x40021400, +0x400>
+
+gpiog: GPIOPort.STM32_GPIOPort @ sysbus <0x40021800, +0x400>
+
+gpioh: GPIOPort.STM32_GPIOPort @ sysbus <0x40021c00, +0x400>
+
+// autogenerated
+
+userled: Miscellaneous.LED @ gpioc 0xd
+    invert: true
+
+gpioc:
+    13 -> userled@0
+
+nvic0: IRQControllers.NVIC @ {
+    sysbus new Bus.BusPointRegistration { address: 0xe000e000; cpu: cpu0 }
+}
+    -> cpu0@0
+
+cpu0: CPU.CortexM @ sysbus
+    cpuType: "cortex-m4f"
+    nvic: nvic0
+
+i2c1: I2C.STM32F4_I2C @ sysbus 0x40005400
+    EventInterrupt->nvic0@31
+    ErrorInterrupt->nvic0@32
+
+rtc: Timers.STM32F4_RTC @ sysbus 0x40002800
+    AlarmIRQ->nvic0@41
+
+rcc: Miscellaneous.STM32F4_RCC @ sysbus 0x40023800
+    rtcPeripheral: rtc
+
+spi1: SPI.STM32SPI @ sysbus 0x40013000
+    IRQ->nvic0@35
+
+adc1: Analog.STM32_ADC @ sysbus 0x40012000
+    IRQ->nvic0@18
+
+// st,stm32f4 overlay
+
+sysbus:
+    init:
+        ApplySVD @https://dl.antmicro.com/projects/renode/svd/STM32F40x.svd.gz
+
+// cortex-m overlay
+
+dwt: Miscellaneous.DWT @ sysbus 0xE0001000
+    frequency: 72000000
+
+systemc: SystemC.SystemCPeripheral @ sysbus <0x9000000, +0xffffff>
+    address: "127.0.0.1"
+    timeSyncPeriodUS: 5000
+    0 -> nvic0@55
+    1 -> nvic0@56
diff --git a/tests/platforms/systemc/interrupts/interrupts.robot b/tests/platforms/systemc/interrupts/interrupts.robot
new file mode 100644
index 0000000..90d07d8
--- /dev/null
+++ b/tests/platforms/systemc/interrupts/interrupts.robot
@@ -0,0 +1,22 @@
+*** Variables ***
+${ZEPHYR_BINARY}                    @https://dl.antmicro.com/projects/renode/systemc-examples-zephyr-interrupts-stm32f401_mini.elf-s_573712-209c153accbf90335274ee5ae16eb9abb74d7808
+${SYSTEMC_BINARY}                   @https://dl.antmicro.com/projects/renode/x64-systemc--interrupts.elf-s_1017672-89c2d9745bc1fbc82c3ee727ae0d248415ed6d35
+${PLATFORM}                         @tests/platforms/systemc/interrupts/interrupts.repl
+${UART}                             sysbus.usart1
+
+*** Keywords ***
+Create Machine
+    Execute Command                 mach create
+    Execute Command                 machine LoadPlatformDescription ${PLATFORM}
+    Execute Command                 sysbus LoadELF ${ZEPHYR_BINARY}
+    Execute Command                 sysbus.systemc SystemCExecutablePath ${SYSTEMC_BINARY}
+
+*** Test Cases ***
+Should Invoke Interrupt Handlers Initiated By SystemC
+    [Tags]                          skip_windows    skip_osx
+    Create Machine
+    Create Terminal Tester          ${UART}
+    Start Emulation
+
+    Wait For Line On Uart           Interrupt handler for interrupter 0 (every 1 second)
+    Wait For Line On Uart           Interrupt handler for interrupter 1 (every 3 seconds)
diff --git a/tests/platforms/systemc/multiple-peripherals/multiple-peripherals.repl b/tests/platforms/systemc/multiple-peripherals/multiple-peripherals.repl
new file mode 100644
index 0000000..b1eee35
--- /dev/null
+++ b/tests/platforms/systemc/multiple-peripherals/multiple-peripherals.repl
@@ -0,0 +1,95 @@
+flash0: Memory.MappedMemory @ sysbus 0x8000000
+    size: 0x40000
+
+sram0: Memory.MappedMemory @ sysbus 0x20000000
+    size: 0x10000
+
+timers4: Timers.STM32_Timer @ sysbus <0x40000800, +0x400>
+    frequency: 10000000
+    initialLimit: 0xffffffff
+    ->nvic0@30
+
+usart1: UART.STM32_UART @ sysbus <0x40011000, +0x400>
+    ->nvic0@37
+
+gpioa: GPIOPort.STM32_GPIOPort @ sysbus <0x40020000, +0x400>
+
+gpiob: GPIOPort.STM32_GPIOPort @ sysbus <0x40020400, +0x400>
+
+gpioc: GPIOPort.STM32_GPIOPort @ sysbus <0x40020800, +0x400>
+
+gpiod: GPIOPort.STM32_GPIOPort @ sysbus <0x40020c00, +0x400>
+
+gpioe: GPIOPort.STM32_GPIOPort @ sysbus <0x40021000, +0x400>
+
+gpiof: GPIOPort.STM32_GPIOPort @ sysbus <0x40021400, +0x400>
+
+gpiog: GPIOPort.STM32_GPIOPort @ sysbus <0x40021800, +0x400>
+
+gpioh: GPIOPort.STM32_GPIOPort @ sysbus <0x40021c00, +0x400>
+
+// autogenerated
+
+userled: Miscellaneous.LED @ gpioc 0xd
+    invert: true
+
+gpioc:
+    13 -> userled@0
+
+nvic0: IRQControllers.NVIC @ {
+    sysbus new Bus.BusPointRegistration { address: 0xe000e000; cpu: cpu0 }
+}
+    -> cpu0@0
+
+cpu0: CPU.CortexM @ sysbus
+    cpuType: "cortex-m4f"
+    nvic: nvic0
+
+i2c1: I2C.STM32F4_I2C @ sysbus 0x40005400
+    EventInterrupt->nvic0@31
+    ErrorInterrupt->nvic0@32
+
+rtc: Timers.STM32F4_RTC @ sysbus 0x40002800
+    AlarmIRQ->nvic0@41
+
+rcc: Miscellaneous.STM32F4_RCC @ sysbus 0x40023800
+    rtcPeripheral: rtc
+
+spi1: SPI.STM32SPI @ sysbus 0x40013000
+    IRQ->nvic0@35
+
+adc1: Analog.STM32_ADC @ sysbus 0x40012000
+    IRQ->nvic0@18
+
+// st,stm32f4 overlay
+
+sysbus:
+    init:
+        ApplySVD @https://dl.antmicro.com/projects/renode/svd/STM32F40x.svd.gz
+
+// cortex-m overlay
+
+dwt: Miscellaneous.DWT @ sysbus 0xE0001000
+    frequency: 72000000
+
+systemc_peripheral_A: SystemC.SystemCPeripheral @ sysbus <0x7000000, +0xfffff>
+    address: "127.0.0.1"
+    timeSyncPeriodUS: 5000
+    init:
+        AddDirectConnection 0 systemc_peripheral_B
+        AddDirectConnection 1 systemc_peripheral_B
+
+systemc_peripheral_B: SystemC.SystemCPeripheral @ sysbus <0x7100000, +0xfffff>
+    address: "127.0.0.1"
+    timeSyncPeriodUS: 5000
+    init:
+        AddDirectConnection 0 systemc_peripheral_C
+        AddDirectConnection 1 systemc_peripheral_C
+
+systemc_peripheral_C: SystemC.SystemCPeripheral @ sysbus <0x7200000, +0xfffff>
+    address: "127.0.0.1"
+    timeSyncPeriodUS: 5000
+    init:
+        AddDirectConnection 0 systemc_peripheral_A
+        AddDirectConnection 1 systemc_peripheral_A
+    0 -> nvic0@57
diff --git a/tests/platforms/systemc/multiple-peripherals/multiple-peripherals.robot b/tests/platforms/systemc/multiple-peripherals/multiple-peripherals.robot
new file mode 100644
index 0000000..824e9ef
--- /dev/null
+++ b/tests/platforms/systemc/multiple-peripherals/multiple-peripherals.robot
@@ -0,0 +1,23 @@
+*** Variables ***
+${SYSTEMC_BINARY}                   @https://dl.antmicro.com/projects/renode/x64-systemc--multiple-peripherals.elf-s_1045808-e4358ec1f6d94e52e0d946c5c6acb80e157523b5
+${ZEPHYR_BINARY}                    @https://dl.antmicro.com/projects/renode/systemc-examples-zephyr-multiple-peripherals-stm32f401_mini.elf-s_574416-8bc080d4e6c922e7c89a233d1f74f6d9bcb45274
+${PLATFORM}                         @tests/platforms/systemc/multiple-peripherals/multiple-peripherals.repl
+${UART}                             sysbus.usart1
+
+*** Keywords ***
+Create Machine
+    Execute Command                 mach create
+    Execute Command                 machine LoadPlatformDescription ${PLATFORM}
+    Execute Command                 sysbus.systemc_peripheral_A SystemCExecutablePath ${SYSTEMC_BINARY}
+    Execute Command                 sysbus.systemc_peripheral_B SystemCExecutablePath ${SYSTEMC_BINARY}
+    Execute Command                 sysbus.systemc_peripheral_C SystemCExecutablePath ${SYSTEMC_BINARY}
+    Execute Command                 sysbus LoadELF ${ZEPHYR_BINARY}
+
+*** Test Cases ***
+Should Run The Multiple Peripherals Example
+    [Tags]                          skip_windows    skip_osx
+    Create Machine
+    Create Terminal Tester          ${UART}
+    Start Emulation
+
+    Wait For Line On Uart           Example complete!
diff --git a/tests/platforms/systemc/test-synchronization/ExecuteInLockPeripheral.cs b/tests/platforms/systemc/test-synchronization/ExecuteInLockPeripheral.cs
new file mode 100644
index 0000000..d04091e
--- /dev/null
+++ b/tests/platforms/systemc/test-synchronization/ExecuteInLockPeripheral.cs
@@ -0,0 +1,42 @@
+using Antmicro.Renode.Core;
+using Antmicro.Renode.Logging;
+using Antmicro.Renode.Time;
+using Antmicro.Renode.Peripherals.Bus;
+
+namespace Antmicro.Renode.Peripherals.Test
+{
+    class ExecuteInLockPeripheral : IBytePeripheral, IKnownSize
+    {
+        public ExecuteInLockPeripheral(Machine machine)
+        {
+            this.machine = machine;
+        }
+
+        public void Reset()
+        {
+        }
+
+        public byte ReadByte(long offset)
+        {
+            return 0;
+        }
+
+        public void WriteByte(long offset, byte value)
+        {
+            machine.ClockSource.ExecuteInLock(() =>
+                {
+                    this.Log(LogLevel.Info, $"Got write request with value 0x{value:X}");
+                });
+        }
+
+        public long Size
+        {
+            get
+            {
+                return 1;
+            }
+        }
+
+        private Machine machine;
+    }
+}
diff --git a/tests/platforms/systemc/test-synchronization/synchronization.resc b/tests/platforms/systemc/test-synchronization/synchronization.resc
new file mode 100644
index 0000000..28d268b
--- /dev/null
+++ b/tests/platforms/systemc/test-synchronization/synchronization.resc
@@ -0,0 +1,10 @@
+using sysbus
+mach create "synchronization_test"
+
+include $ORIGIN/MockPeripheralWithExecuteInLock.cs
+machine LoadPlatformDescription $ORIGIN/synchronization.repl
+
+sysbus LogPeripheralAccess writer_systemc true
+sysbus LogPeripheralAccess test true
+sysbus.writer_systemc SystemCExecutablePath $ORIGIN/../bin/synchronization
+
diff --git a/tests/platforms/systemc/test-synchronization/synchronization.robot b/tests/platforms/systemc/test-synchronization/synchronization.robot
new file mode 100644
index 0000000..2ed2782
--- /dev/null
+++ b/tests/platforms/systemc/test-synchronization/synchronization.robot
@@ -0,0 +1,24 @@
+*** Variables ***
+${SYSTEMC_BINARY}                    @https://dl.antmicro.com/projects/renode/x64-systemc--test-synchronization.elf-s_606984-4b970b9b9da67d412220cc827634accb89619139
+${EXECUTE_IN_LOCK_PERIPHERAL}        @tests/platforms/systemc/test-synchronization/ExecuteInLockPeripheral.cs
+${PLATFORM}=     SEPARATOR=
+...  """                                                                            ${\n}
+...  test: Test.ExecuteInLockPeripheral @ sysbus 0x1000000                          ${\n}
+...                                                                                 ${\n} 
+...  writer_systemc: SystemC.SystemCPeripheral @ sysbus <0x9000000, +0xffffff>      ${\n}
+...  ${SPACE*4}address: "127.0.0.1"                                                       ${\n}
+...  ${SPACE*4}timeSyncPeriodUS: 5000                                                     ${\n}
+...  """ 
+
+*** Test Cases ***
+Should Not Deadlock Writing To ExecuteInLockPeripheral
+    [Tags]                          skip_windows    skip_osx
+    Execute Command                 using sysbus
+    Execute Command                 mach create
+    Execute Command                 include ${EXECUTE_IN_LOCK_PERIPHERAL}
+    Execute Command                 machine LoadPlatformDescriptionFromString ${PLATFORM} 
+    Execute Command                 sysbus.writer_systemc SystemCExecutablePath ${SYSTEMC_BINARY}
+    Create Log Tester               1
+    Start Emulation
+
+    Wait For Log Entry              Got write request with value 0xAB
diff --git a/tests/platforms/systemc/transaction-delay/transaction-delay.repl b/tests/platforms/systemc/transaction-delay/transaction-delay.repl
new file mode 100644
index 0000000..d6d9c34
--- /dev/null
+++ b/tests/platforms/systemc/transaction-delay/transaction-delay.repl
@@ -0,0 +1,77 @@
+flash0: Memory.MappedMemory @ sysbus 0x8000000
+    size: 0x40000
+
+sram0: Memory.MappedMemory @ sysbus 0x20000000
+    size: 0x10000
+
+timers4: Timers.STM32_Timer @ sysbus <0x40000800, +0x400>
+    frequency: 10000000
+    initialLimit: 0xffffffff
+    ->nvic0@30
+
+usart1: UART.STM32_UART @ sysbus <0x40011000, +0x400>
+    ->nvic0@37
+
+gpioa: GPIOPort.STM32_GPIOPort @ sysbus <0x40020000, +0x400>
+
+gpiob: GPIOPort.STM32_GPIOPort @ sysbus <0x40020400, +0x400>
+
+gpioc: GPIOPort.STM32_GPIOPort @ sysbus <0x40020800, +0x400>
+
+gpiod: GPIOPort.STM32_GPIOPort @ sysbus <0x40020c00, +0x400>
+
+gpioe: GPIOPort.STM32_GPIOPort @ sysbus <0x40021000, +0x400>
+
+gpiof: GPIOPort.STM32_GPIOPort @ sysbus <0x40021400, +0x400>
+
+gpiog: GPIOPort.STM32_GPIOPort @ sysbus <0x40021800, +0x400>
+
+gpioh: GPIOPort.STM32_GPIOPort @ sysbus <0x40021c00, +0x400>
+
+// autogenerated
+
+userled: Miscellaneous.LED @ gpioc 0xd
+    invert: true
+
+gpioc:
+    13 -> userled@0
+
+nvic0: IRQControllers.NVIC @ {
+    sysbus new Bus.BusPointRegistration { address: 0xe000e000; cpu: cpu0 }
+}
+    -> cpu0@0
+
+cpu0: CPU.CortexM @ sysbus
+    cpuType: "cortex-m4f"
+    nvic: nvic0
+
+i2c1: I2C.STM32F4_I2C @ sysbus 0x40005400
+    EventInterrupt->nvic0@31
+    ErrorInterrupt->nvic0@32
+
+rtc: Timers.STM32F4_RTC @ sysbus 0x40002800
+    AlarmIRQ->nvic0@41
+
+rcc: Miscellaneous.STM32F4_RCC @ sysbus 0x40023800
+    rtcPeripheral: rtc
+
+spi1: SPI.STM32SPI @ sysbus 0x40013000
+    IRQ->nvic0@35
+
+adc1: Analog.STM32_ADC @ sysbus 0x40012000
+    IRQ->nvic0@18
+
+// st,stm32f4 overlay
+
+sysbus:
+    init:
+        ApplySVD @https://dl.antmicro.com/projects/renode/svd/STM32F40x.svd.gz
+
+// cortex-m overlay
+
+dwt: Miscellaneous.DWT @ sysbus 0xE0001000
+    frequency: 72000000
+
+systemc: SystemC.SystemCPeripheral @ sysbus <0x9000000, 0x9000001>
+    address: "127.0.0.1"
+    timeSyncPeriodUS: 5000
diff --git a/tests/platforms/systemc/transaction-delay/transaction-delay.robot b/tests/platforms/systemc/transaction-delay/transaction-delay.robot
new file mode 100644
index 0000000..9f47da5
--- /dev/null
+++ b/tests/platforms/systemc/transaction-delay/transaction-delay.robot
@@ -0,0 +1,32 @@
+*** Variables ***
+${SYSTEMC_BINARY}                   @https://dl.antmicro.com/projects/renode/x64-systemc--transaction-delay.elf-s_730520-71a2e317e0f42799b7679af5dba036e918a7146e
+${ZEPHYR_BINARY}                    @https://dl.antmicro.com/projects/renode/systemc-examples-zephyr-transaction-delay-stm32f401_mini.elf-s_572316-f591f8f491f1f3480aa6f7284d2bdbef3bebb8d5
+${UART}                             sysbus.usart1
+
+*** Keywords ***
+Create Machine
+    Execute Command                 mach create
+    Execute Command                 machine LoadPlatformDescription @tests/platforms/systemc/transaction-delay/transaction-delay.repl
+    Execute Command                 sysbus LoadELF ${ZEPHYR_BINARY}
+    Execute Command                 systemc SystemCExecutablePath ${SYSTEMC_BINARY}
+
+Virtual Time Should Be Equal To
+    [Arguments]                     ${time_string}
+    ${res}=                         Execute Command  machine ElapsedVirtualTime
+    Should Contain                  ${res}  Elapsed Virtual Time: ${time_string}
+
+*** Test Cases ***
+Should Respect SystemC Transaction Durations
+    [Tags]                          skip_windows    skip_osx
+    Create Machine
+    Create Terminal Tester          ${UART}
+    Start Emulation
+
+    Wait For Line On Uart           SystemC virtual time (1s transaction delay): 1 s
+    Virtual Time Should Be Equal To  00:00:02
+
+    Wait For Line On Uart           SystemC virtual time (1s transaction delay): 2 s
+    Virtual Time Should Be Equal To  00:00:03
+
+    Wait For Line On Uart           SystemC virtual time (1s transaction delay): 6 s
+    Virtual Time Should Be Equal To  00:00:07
diff --git a/tests/tests.yaml b/tests/tests.yaml
index b26b7bc..2bbadc2 100644
--- a/tests/tests.yaml
+++ b/tests/tests.yaml
@@ -179,3 +179,7 @@
 - tests/platforms/MSP430X.robot
 - tests/unit-tests/riscv-zve-extension.robot
 - tests/unit-tests/sync_pc.robot
+- tests/platforms/systemc/interrupts/interrupts.robot
+- tests/platforms/systemc/multiple-peripherals/multiple-peripherals.robot
+- tests/platforms/systemc/transaction-delay/transaction-delay.robot
+- tests/platforms/systemc/test-synchronization/synchronization.robot