public inbox for qemu-devel@nongnu.org
 help / color / mirror / Atom feed
From: Tao Ding <dingtao0430@163.com>
To: Peter Maydell <peter.maydell@linaro.org>, qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org, Tao Ding <dingtao0430@163.com>
Subject: [PATCH v1 0/5] PL011 and PL080 for dma transfer
Date: Sat, 21 Mar 2026 23:46:55 +0800	[thread overview]
Message-ID: <cover.1774099044.git.dingtao0430@163.com> (raw)

The PL080 implementation in QEMU currently supports memory-to-memory data transfers 
but lacks support for peripheral-related data transfers. 

To enable this functionality, signal interfaces have been added for PL011 and PL080 to 
emulate the DMA request handshake, thereby completing the peripheral-related 
transfer features that were previously unimplemented in PL080. Specifically, 
memory-to-peripheral, peripheral-to-memory and periphral-to-periphral under dma controll.

Although PL011 and PL080 are not strongly coupled, for the convenience of verification, 
they are still placed in the same patch thread. Finally, unit testing was conducted using Qtest, 
and Linux system testing was conducted on "Versatilepb" machine in Qemu.

Tao Ding (5):
  Fixed the PL011 FIFO level bug
  PL011 generate receive timeout interrupt
  Implement the DMA interface of PL011
  Run pl080_run in main loop
  PL080 add transfer memory and peripheral

 hw/char/pl011.c         |  72 ++++++++++++++++++++++---
 hw/dma/pl080.c          | 116 ++++++++++++++++++++++++++++++++++------
 include/hw/char/pl011.h |   1 +
 include/hw/dma/pl080.h  |   7 +++
 4 files changed, 174 insertions(+), 22 deletions(-)


base-commit: 8e711856d7639cbffa51405f2cc2366e3d9e3a23

Testing:
========

Testing prepare:
	1. The changes in the patch need to be applied. 
	2. Then connect the DMA signals of PL011 and PL080 in the Versatilepb board.
		Channel0 is used to do UART's TX, memory to peripheral. Use 0 signal index.
		Channel1 is used to do UART's RX, peripheral to memory. Use 1 signal index.
		(The connection of the signal needs to consider the offset in the device.)

Change in Versatilepb.c :
-----------------------------------------
diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c
index 254b1610b3..ee0056e831 100644
--- a/hw/arm/versatilepb.c
+++ b/hw/arm/versatilepb.c
@@ -281,7 +281,7 @@ static void versatile_init(MachineState *machine, int board_id)
     }
 
     pl011_create(0x101f1000, pic[12], serial_hd(0));
-    pl011_create(0x101f2000, pic[13], serial_hd(1));
+    DeviceState *pl011_dev = pl011_create(0x101f2000, pic[13], serial_hd(1));
     pl011_create(0x101f3000, pic[14], serial_hd(2));
     pl011_create(0x10009000, sic[6], serial_hd(3));
 
@@ -292,6 +292,15 @@ static void versatile_init(MachineState *machine, int board_id)
     sysbus_realize_and_unref(busdev, &error_fatal);
     sysbus_mmio_map(busdev, 0, 0x10130000);
     sysbus_connect_irq(busdev, 0, pic[17]);
+    sysbus_connect_irq(busdev, 3, qdev_get_gpio_in(pl011_dev, 0));
+    sysbus_connect_irq(busdev, 4, qdev_get_gpio_in(pl011_dev, 1));
+
+    sysbus_connect_irq(SYS_BUS_DEVICE(pl011_dev), 6, qdev_get_gpio_in(dev, 0));
+    sysbus_connect_irq(SYS_BUS_DEVICE(pl011_dev), 7, qdev_get_gpio_in(dev, 1));
+
+    sysbus_connect_irq(SYS_BUS_DEVICE(pl011_dev), 8, qdev_get_gpio_in(dev, 16));
+    sysbus_connect_irq(SYS_BUS_DEVICE(pl011_dev), 9, qdev_get_gpio_in(dev, 17));
+
 
     sysbus_create_simple("sp804", 0x101e2000, pic[4]);
     sysbus_create_simple("sp804", 0x101e3000, pic[5]);
-----------------------------------------

Unit Test by QTest:
	1. create pl080-test.c in tests/qtest directory.
------------ pl080-test.c ------------
#include "qemu/osdep.h"
#include "libqtest-single.h"

#define PL011_BASE   0x101f2000
#define PL080_BASE   0x10130000
#define RAM_BASE     0x0

/* ---------------- PL011 Registers ---------------- */
#define UARTDR     0x00
#define UARTFR     0x18
#define UARTCR     0x30
#define UARTLCR_H  0x2C
#define UARTDMACR  0x48
/* UARTFR bits */
#define FR_RXFE  (1 << 4)
#define FR_TXFF  (1 << 5)

/* UARTCR bits */
#define CR_UARTEN (1 << 0)
#define CR_TXE    (1 << 8)
#define CR_RXE    (1 << 9)
#define CR_LBE    (1 << 7)

#define LCRH_FEN (1 << 4)   // FIFO enable

/* ---------------- PL080 DMA Registers ---------------- */
#define DMAC_CONFIG       0x30

/* Channel offsets */
#define DMA_CH_SRC        0x00
#define DMA_CH_DST        0x04
#define DMA_CH_LLI        0x08
#define DMA_CH_CTRL       0x0C
#define DMA_CH_CFG        0x10

/* Control Register Fields */
#define CTRL_TRANSFER_SIZE(x)   ((x) & 0xFFF)
#define CTRL_SRC_WIDTH(x)       ((x) << 18)
#define CTRL_DST_WIDTH(x)       ((x) << 21)
#define CTRL_SRC_INC            (1 << 26)
#define CTRL_DST_INC            (1 << 27)

/* Width */
#define WIDTH_8BIT   0
#define WIDTH_16BIT  1
#define WIDTH_32BIT  2

/* Config Register Fields */
#define CFG_ENABLE           (1 << 0)

/* Flow control */
#define CFG_FLOW_MEM2MEM     (0 << 11)
#define CFG_FLOW_MEM2PER     (1 << 11)
#define CFG_FLOW_PER2MEM     (2 << 11)
#define CFG_FLOW_PER2PER     (3 << 11)

/* Peripheral IDs (VersatilePB, PL011 UART) */
#define PERIPH_UART_TX       0 
#define PERIPH_UART_RX       1

/* Macro to set peripheral in CFG */
#define CFG_DST_PERIPH(x)    ((x) << 6)   // DST peripheral ID
#define CFG_SRC_PERIPH(x)    ((x) << 1)   // SRC peripheral ID

static void test_pl080_pl011(void)
{
    QTestState *s;
    uint32_t cr;
    uint32_t i;

    /* --------------------------- */
    /* 1.  UART(loopback) */
    /* --------------------------- */
    s = qtest_start("-machine versatilepb -d guest_errors,trace:memory_region_ops_* -D qemu.log");

    qtest_writel(s, PL011_BASE + UARTCR, 0);
    qtest_writel(s, PL011_BASE + UARTLCR_H, LCRH_FEN);

    cr = CR_UARTEN | CR_TXE | CR_RXE | CR_LBE;
    qtest_writel(s, PL011_BASE + UARTCR, cr);

    qtest_writel(s, PL011_BASE + UARTDMACR, 0x3);

    /* --------------------------- */
    /* 2. data in memory */
    /* --------------------------- */
    uint8_t tx_buff[12] = {
        0x1,0x2,0x3,0x4,
        0x5,0x6,0x7,0x8,
        0x9,0xa,0xb,0xc
    };

    uint32_t src_addr = RAM_BASE + 0x1000;
    uint32_t dst_addr = RAM_BASE + 0x2000;
    int len = 12;

    for (i = 0; i < len; i++) {
        qtest_writeb(s, src_addr + i, tx_buff[i]);  
        qtest_writeb(s, dst_addr + i, 0);           
    }

    /* --------------------------- */
    /* 3. enable DMA controller */
    /* --------------------------- */
    qtest_writel(s, PL080_BASE + DMAC_CONFIG, 1);

    /* --------------------------- */
    /* 4. DMA channel 0: mem -> UART */
    /* --------------------------- */
    uint32_t ch0_base = PL080_BASE + 0x100;

    qtest_writel(s, ch0_base + DMA_CH_SRC, src_addr);
    qtest_writel(s, ch0_base + DMA_CH_DST, PL011_BASE + UARTDR);

    qtest_writel(s, ch0_base + DMA_CH_CTRL,
        CTRL_TRANSFER_SIZE(len/4) |
        CTRL_SRC_WIDTH(WIDTH_32BIT) |
        CTRL_DST_WIDTH(WIDTH_8BIT) |
        CTRL_SRC_INC
    );

    /* CFG: enable + flow + destination peripheral */
    qtest_writel(s, ch0_base + DMA_CH_CFG,
        CFG_ENABLE |
        CFG_FLOW_MEM2PER |
        CFG_DST_PERIPH(PERIPH_UART_TX)
    );

    /* --------------------------- */
    /* 5. DMA channel 1: UART -> mem */
    /* --------------------------- */
    uint32_t ch1_base = PL080_BASE + 0x120;

    qtest_writel(s, ch1_base + DMA_CH_SRC, PL011_BASE + UARTDR);
    qtest_writel(s, ch1_base + DMA_CH_DST, dst_addr);

    qtest_writel(s, ch1_base + DMA_CH_CTRL,
        CTRL_TRANSFER_SIZE(len) |
        CTRL_SRC_WIDTH(WIDTH_8BIT) |
        CTRL_DST_WIDTH(WIDTH_32BIT) |
        CTRL_DST_INC
    );

    /* CFG: enable + flow + source peripheral */
    qtest_writel(s, ch1_base + DMA_CH_CFG,
        CFG_ENABLE |
        CFG_FLOW_PER2MEM |
        CFG_SRC_PERIPH(PERIPH_UART_RX)
    );

    /* --------------------------- */
    /* 6. DMA run*/
    /* --------------------------- */
    clock_step(1000000);

    
    /* wait Channel 0 */
    while (true) {
        uint32_t cfg = qtest_readl(s, ch0_base + DMA_CH_CFG);
        if (!(cfg & CFG_ENABLE)) {
            printf("Channel 0 finish\n");
            break;
        }
        clock_step(1000000);  
    }

    /* wait Channel 1 */
    while (true) {
        uint32_t cfg = qtest_readl(s, ch1_base + DMA_CH_CFG);
        if (!(cfg & CFG_ENABLE)) {
            printf("Channel 1 finish\n");
            break;
        }
        clock_step(1000000);  
    }

    /* --------------------------- */
    /* 7. check data */
    /* --------------------------- */
    for (i = 0; i < len; i++) {
        uint8_t s_val = qtest_readb(s, src_addr + i);
        uint8_t d_val = qtest_readb(s, dst_addr + i);

        g_assert_cmpuint(s_val, ==, d_val);
    }

    qtest_end();
}

int main(int argc, char **argv)
{
    g_test_init(&argc, &argv, NULL);
    qtest_add_func("/pl080/test1", test_pl080_pl011);
    return g_test_run();
}
-----------------------------------------
	2. modify meson
-----------------------------------------
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index be4fa627b5..6ea187517f 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -249,7 +249,8 @@ qtests_arm = \
   (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') and
    config_all_devices.has_key('CONFIG_DM163')? ['dm163-test'] : []) + \
   ['arm-cpu-features',
-   'boot-serial-test']
+   'boot-serial-test'] + \
+  ['pl080-test']
 
 # TODO: once aarch64 TCG is fixed on ARM 32 bit host, make bios-tables-test unconditional
 qtests_aarch64 = \
-----------------------------------------

Configuration:
	../configure --target-list=arm-softmmu --enable-debug --disable-werror
build:
	make
Run:
	QTEST_QEMU_BINARY=./qemu-system-arm ./tests/qtest/pl080-test
Result:
-----------------------------------------
	TAP version 13
	# random seed: R02Sf2f5311a926a969453439a52838a6fd0
	1..1
	# Start of arm tests
	# Start of pl080 tests
	# starting QEMU: exec ./qemu-system-arm -qtest unix:/tmp/qtest-23375.sock 
    -qtest-log /dev/null -chardev socket,path=/tmp/qtest-23375.qmp,id=char0 -mon chardev=char0,mode=control 
    -display none -audio none -run-with exit-with-parent=on -machine versatilepb -d guest_errors,trace:memory_region_ops_* 
    -D qemu.log -accel qtest
	Channel 0 finish
	Channel 1 finish
	ok 1 /arm/pl080/test1
	# End of pl080 tests
	# End of arm tests
-----------------------------------------

System Test:
	1. Refer to qemu/docs/system/arm/volatile.rst to boot Linux.
	2. modify versatile-pb.dts in linux to use dma.
		serial@101f2000 {
			compatible = "arm,pl011\0arm,primecell";
			reg = <0x101f2000 0x1000>;
			interrupts = <0x0d>;
			clocks = <0x05 0x06>;
			clock-names = "uartclk\0apb_pclk";
			auto-poll;
+			dmas = <&dma0 1 1>,
+					<&dma0 0 1>;
+			dma-names = "rx", "tx";
		};

Run:
	./build/qemu-system-arm -M versatilepb -nographic \
	-serial mon:stdio \
	-serial pty \
	-kernel zImage \
	-dtb versatile-pb.dtb \
	-drive if=scsi,driver=file,filename=rootfs.img \
	-append "console=ttyAMA0 rw root=/dev/sda" \
	-d guest_errors -D qemu.log

Send and receive data to the guest's UART in another console of the host by pty.

Test result:

guest:
	~ # echo "hello" > /dev/ttyAMA1
	uart-pl011 101f2000.serial: DMA channel TX dma1chan0
	uart-pl011 101f2000.serial: DMA channel RX dma1chan1
host:
	cat /dev/pts/1
	hello

-- 
2.43.0



             reply	other threads:[~2026-03-21 15:47 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-21 15:46 Tao Ding [this message]
2026-03-21 15:46 ` [PATCH v1 1/5] Fixed the PL011 FIFO level bug Tao Ding
2026-03-23 11:43   ` Peter Maydell
2026-03-21 15:46 ` [PATCH v1 2/5] PL011 generate receive timeout interrupt Tao Ding
2026-03-23 11:49   ` Peter Maydell
2026-03-21 15:46 ` [PATCH v1 3/5] Implement the DMA interface of PL011 Tao Ding
2026-03-21 15:46 ` [PATCH v1 4/5] Run pl080_run in main loop Tao Ding
2026-03-24 14:25   ` Philippe Mathieu-Daudé
2026-03-21 15:47 ` [PATCH v1 5/5] PL080 add transfer memory and peripheral Tao Ding
2026-03-23 11:51   ` Peter Maydell
2026-03-23 15:57     ` Tao Ding
2026-03-23 11:32 ` [PATCH v1 0/5] PL011 and PL080 for dma transfer Peter Maydell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=cover.1774099044.git.dingtao0430@163.com \
    --to=dingtao0430@163.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox