* [PATCH 1/7] hw/dma: add K230 gsdma
2026-07-21 16:13 [PATCH 0/7] K230: add gsdma and decomp_gzip Tao Ding
@ 2026-07-21 16:13 ` Tao Ding
2026-07-21 16:13 ` [PATCH 2/7] hw/riscv: k230: add gsdma in K230 board Tao Ding
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Tao Ding @ 2026-07-21 16:13 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-riscv, pbonzini, palmer, alistair.francis, liwei1518,
daniel.barboza, zhiwei_liu, chao.liu, farosas, lvivier, Tao Ding
K230 GSDMA includes SDMA (System Direct Memory Access) and GDMA (Graphic Direct Memory Access).
In this patch, add SDMA for k230 board. The following features have not been implemented:
1. axi protocol related
2. channel arbitration
3. lower power mode
SDMA supports transfer data between memory, in which case SDMA is the controller.
It also supports transfer data to decomp_gzip and caching it through SRAM.
In this case, decomp_gzip is the controller, which controls SDMA through handshake signals.
According to "K230 Technical Reference Manual v0.3.1" section 2.5.2.
https://download.kendryte.com/developer/k230/HDK/K230%E7%A1%AC%E4%BB%B6%E6%96%87%E6%A1%A3/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf
This commit includes:
- K230 SDMA (System Direct Memory Access) model (k230_gsdma.c, k230_gsdma.h)
- GSDMA trace log (trace-events)
- Kconfig and meson.build
Signed-off-by: Tao Ding <dingtao0430@163.com>
---
hw/dma/Kconfig | 3 +
hw/dma/k230_gsdma.c | 532 ++++++++++++++++++++++++++++++++++++
hw/dma/meson.build | 1 +
hw/dma/trace-events | 6 +
include/hw/dma/k230_gsdma.h | 128 +++++++++
5 files changed, 670 insertions(+)
create mode 100644 hw/dma/k230_gsdma.c
create mode 100644 include/hw/dma/k230_gsdma.h
diff --git a/hw/dma/Kconfig b/hw/dma/Kconfig
index 98fbb1bb04..0a23e7d7b8 100644
--- a/hw/dma/Kconfig
+++ b/hw/dma/Kconfig
@@ -30,3 +30,6 @@ config SIFIVE_PDMA
config XLNX_CSU_DMA
bool
select REGISTER
+
+config K230_GSDMA
+ bool
\ No newline at end of file
diff --git a/hw/dma/k230_gsdma.c b/hw/dma/k230_gsdma.c
new file mode 100644
index 0000000000..1f76f62fa0
--- /dev/null
+++ b/hw/dma/k230_gsdma.c
@@ -0,0 +1,532 @@
+/*
+ * Kendryte K230 GSDMA
+ *
+ * Copyright (c) 2026 Tao Ding <dingtao0430@163.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * GSDMA includes SDMA (System Direct Memory Access, K230 TRM section 10.2) and
+ * GDMA (Graphic Direct Memory Access, K230 TRM section 2.5.2).
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "migration/vmstate.h"
+#include "system/address-spaces.h"
+#include "hw/dma/k230_gsdma.h"
+#include "hw/core/irq.h"
+#include "trace.h"
+
+static void k230_gsdma_update_irq(K230GSDMAState *s)
+{
+ qemu_set_irq(s->irq, !!(s->dma_int_stat & ~s->dma_int_mask));
+}
+
+static bool k230_gsdma_decomp_enabled(K230GSDMAState *s)
+{
+ /* Only sdma channel 0 has decomp enable flag */
+ return !!(s->channels[0].cfg & K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN);
+}
+
+static bool k230_gsdma_read_sdma_llt(hwaddr addr, K230GSDMALLT *llt)
+{
+ bool ok = address_space_read(&address_space_memory, addr,
+ MEMTXATTRS_UNSPECIFIED, llt,
+ sizeof(*llt)) == MEMTX_OK;
+
+ if (ok) {
+ trace_k230_gsdma_read_sdma_llt(addr, le32_to_cpu(llt->cfg),
+ le32_to_cpu(llt->src_addr),
+ le32_to_cpu(llt->line_size),
+ le32_to_cpu(llt->line_cfg),
+ le32_to_cpu(llt->dst_addr),
+ le32_to_cpu(llt->next_llt_addr));
+ } else {
+ trace_k230_gsdma_read_sdma_llt_failed(addr);
+ }
+
+ return ok;
+}
+
+static unsigned int k230_gsdma_usr_data_size(uint32_t cfg)
+{
+ /* decode usr_dat_size in CH_CFG register */
+ switch (extract32(cfg, K230_GSDMA_CH_CFG_USR_DATA_SIZE_SHIFT, 2)) {
+ case 0:
+ return 1;
+ case 1:
+ return 2;
+ default:
+ return 4;
+ }
+}
+
+static bool k230_gsdma_transfer_llt(K230GSDMAChannel *c, const K230GSDMALLT *llt)
+{
+ uint32_t llt_cfg = le32_to_cpu(llt->cfg);
+ hwaddr src = le32_to_cpu(llt->src_addr);
+ hwaddr dst = le32_to_cpu(llt->dst_addr);
+ uint32_t line_size = le32_to_cpu(llt->line_size);
+ uint32_t line_cfg = le32_to_cpu(llt->line_cfg);
+ /* Number of lines required for 2d mode */
+ uint32_t line_num =
+ (llt_cfg & K230_GSDMA_LLT_2D_MODE) ? extract32(line_cfg, 0, 16) : 1;
+ /* Stride line size required for 2d mode */
+ uint32_t line_space = extract32(line_cfg, 16, 16);
+
+ bool dat_mode = c->cfg & K230_GSDMA_CH_CFG_DAT_MODE;
+ bool src_fixed = c->cfg & K230_GSDMA_CH_CFG_SRC_FIXED;
+ bool dst_fixed = c->cfg & K230_GSDMA_CH_CFG_DST_FIXED;
+
+ uint8_t buf[8]; /* K230 sdma axi data width is 64-bit */
+ uint8_t fill[4]; /* Usr data is 32-bit*/
+ unsigned int fill_len = k230_gsdma_usr_data_size(c->cfg);
+ uint32_t i;
+
+ memcpy(fill, &c->usr_data, sizeof(fill));
+ if (dat_mode) {
+ for (i = 0; i < (uint32_t)sizeof(buf); i++) {
+ buf[i] = fill[i % fill_len];
+ }
+ }
+
+ for (i = 0; i < line_num; i++) {
+ uint32_t remaining = line_size;
+ hwaddr line_src = src;
+ hwaddr line_dst = dst;
+
+ while (remaining) {
+ uint32_t chunk = MIN(remaining, (uint32_t)sizeof(buf));
+
+ if (!dat_mode && address_space_read(&address_space_memory, line_src,
+ MEMTXATTRS_UNSPECIFIED, buf,
+ chunk) != MEMTX_OK) {
+ return false;
+ }
+
+ if (address_space_write(&address_space_memory, line_dst,
+ MEMTXATTRS_UNSPECIFIED, buf,
+ chunk) != MEMTX_OK) {
+ return false;
+ }
+
+ if (!dat_mode && !src_fixed) {
+ line_src += chunk;
+ }
+ if (!dst_fixed) {
+ line_dst += chunk;
+ }
+ remaining -= chunk;
+ }
+
+ if (!dat_mode) {
+ if (llt_cfg & K230_GSDMA_LLT_2D_MODE) { /*2d mode*/
+ src += line_size + line_space;
+ } else if (!src_fixed) {
+ src += line_size;
+ }
+ }
+
+ if (!dst_fixed) {
+ dst += line_size;
+ }
+ }
+
+ return true;
+}
+
+static void k230_gsdma_sdma_set_idle(K230GSDMAChannel *c)
+{
+ c->status &= ~(K230_GSDMA_SDMA_STATUS_BUSY | K230_GSDMA_SDMA_STATUS_PAUSE);
+ c->next_llt = 0;
+}
+
+static void k230_gsdma_sdma_set_paused(K230GSDMAChannel *c, hwaddr next_llt)
+{
+ c->status &= ~K230_GSDMA_SDMA_STATUS_BUSY;
+ c->status |= K230_GSDMA_SDMA_STATUS_PAUSE;
+ c->next_llt = next_llt;
+}
+
+static void k230_gsdma_run_sdma(K230GSDMAState *s, unsigned int ch, hwaddr addr)
+{
+ K230GSDMAChannel *c = &s->channels[ch];
+
+ c->status &= ~K230_GSDMA_SDMA_STATUS_PAUSE;
+ c->status |= K230_GSDMA_SDMA_STATUS_BUSY;
+ c->next_llt = 0;
+ c->current_llt = addr;
+
+ while (addr) {
+ K230GSDMALLT llt;
+ uint32_t cfg;
+ hwaddr next;
+
+ if (!k230_gsdma_read_sdma_llt(addr, &llt)) {
+ k230_gsdma_sdma_set_idle(c);
+ return;
+ }
+
+ cfg = le32_to_cpu(llt.cfg);
+ next = le32_to_cpu(llt.next_llt_addr);
+ c->current_llt = addr;
+
+ if (!k230_gsdma_transfer_llt(c, &llt)) {
+ k230_gsdma_sdma_set_idle(c);
+ return;
+ }
+
+ if (cfg & K230_GSDMA_LLT_NODE_INTR) {
+ s->dma_int_stat |= K230_GSDMA_SDMA_ITEM_INT(ch);
+ }
+
+ if (cfg & K230_GSDMA_LLT_PAUSE) {
+ s->dma_int_stat |= K230_GSDMA_SDMA_PAUSE_INT(ch);
+ k230_gsdma_sdma_set_paused(c, next);
+ k230_gsdma_update_irq(s);
+ return;
+ }
+
+ addr = next;
+ }
+
+ s->dma_int_stat |= K230_GSDMA_SDMA_DONE_INT(ch);
+ k230_gsdma_sdma_set_idle(c);
+ k230_gsdma_update_irq(s);
+}
+
+/* Decomp gzip request step sdma */
+static void k230_gsdma_step_sdma(K230GSDMAState *s, unsigned int ch, hwaddr addr)
+{
+ K230GSDMAChannel *c = &s->channels[ch];
+ K230GSDMALLT llt;
+ uint32_t cfg;
+ hwaddr next;
+
+ if (!addr || !k230_gsdma_read_sdma_llt(addr, &llt)) {
+ k230_gsdma_sdma_set_idle(c);
+ return;
+ }
+
+ cfg = le32_to_cpu(llt.cfg);
+ next = le32_to_cpu(llt.next_llt_addr);
+
+ c->status |= K230_GSDMA_SDMA_STATUS_BUSY;
+ c->status &= ~K230_GSDMA_SDMA_STATUS_PAUSE;
+ c->current_llt = addr;
+
+ if (!k230_gsdma_transfer_llt(c, &llt)) {
+ k230_gsdma_sdma_set_idle(c);
+ return;
+ }
+
+ if (cfg & K230_GSDMA_LLT_NODE_INTR) {
+ s->dma_int_stat |= K230_GSDMA_SDMA_ITEM_INT(ch);
+ }
+
+ c->status &= ~K230_GSDMA_SDMA_STATUS_BUSY;
+ c->next_llt = next;
+ if (next == 0) {
+ s->dma_int_stat |= K230_GSDMA_SDMA_DONE_INT(ch);
+ }
+
+ k230_gsdma_update_irq(s);
+}
+
+static const VMStateDescription vmstate_k230_gsdma_channel = {
+ .name = "k230.gsdma.channel",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(ctl, K230GSDMAChannel),
+ VMSTATE_UINT32(status, K230GSDMAChannel),
+ VMSTATE_UINT32(cfg, K230GSDMAChannel),
+ VMSTATE_UINT32(usr_data, K230GSDMAChannel),
+ VMSTATE_UINT32(llt_saddr, K230GSDMAChannel),
+ VMSTATE_UINT32(current_llt, K230GSDMAChannel),
+ VMSTATE_UINT32(next_llt, K230GSDMAChannel),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_k230_gsdma = {
+ .name = "k230.gsdma",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(dma_ch_en, K230GSDMAState),
+ VMSTATE_UINT32(dma_int_mask, K230GSDMAState),
+ VMSTATE_UINT32(dma_int_stat, K230GSDMAState),
+ VMSTATE_UINT32(dma_cfg, K230GSDMAState),
+ VMSTATE_UINT32(dma_weight, K230GSDMAState),
+ VMSTATE_STRUCT_ARRAY(channels, K230GSDMAState,
+ K230_GSDMA_NUM_SDMA_CHANNELS, 2,
+ vmstate_k230_gsdma_channel, K230GSDMAChannel),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static uint64_t k230_gsdma_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ K230GSDMAState *s = K230_GSDMA(opaque);
+ unsigned int ch;
+ hwaddr ch_off;
+ uint64_t ret = 0;
+
+ switch (addr) {
+ case K230_GSDMA_DMA_CH_EN:
+ ret = s->dma_ch_en;
+ break;
+ case K230_GSDMA_DMA_INT_MASK:
+ ret = s->dma_int_mask;
+ break;
+ case K230_GSDMA_DMA_INT_STAT:
+ ret = s->dma_int_stat;
+ break;
+ case K230_GSDMA_DMA_CFG:
+ ret = s->dma_cfg;
+ break;
+ case K230_GSDMA_GDMA_CTRL:
+ case K230_GSDMA_GDMA_LLI_BASE:
+ case K230_GSDMA_GDMA_CURRENT_LLT:
+ case K230_GSDMA_GDMA_CH_CNT0 ... K230_GSDMA_GDMA_CH_CNT_LAST:
+ break;
+ case K230_GSDMA_DMA_WEIGHT:
+ ret = s->dma_weight;
+ break;
+ default:
+ break;
+ }
+
+ ch = (addr - K230_GSDMA_CH_BASE) / K230_GSDMA_CH_STRIDE;
+ ch_off = (addr - K230_GSDMA_CH_BASE) % K230_GSDMA_CH_STRIDE;
+ if (ch < K230_GSDMA_NUM_SDMA_CHANNELS) {
+ switch (ch_off) {
+ case K230_GSDMA_CH_CTL:
+ break;
+ case K230_GSDMA_CH_STATUS:
+ ret = s->channels[ch].status;
+ break;
+ case K230_GSDMA_CH_CFG:
+ ret = s->channels[ch].cfg;
+ break;
+ case K230_GSDMA_CH_USR_DATA:
+ ret = s->channels[ch].usr_data;
+ break;
+ case K230_GSDMA_CH_LLT_SADDR:
+ ret = s->channels[ch].llt_saddr;
+ break;
+ case K230_GSDMA_CH_CURRENT_LLT:
+ ret = s->channels[ch].current_llt;
+ break;
+ default:
+ break;
+ }
+ }
+
+ trace_k230_gsdma_read(addr, size, ret);
+ return ret;
+}
+
+static void k230_gsdma_write_gdma(K230GSDMAState *s, hwaddr addr, uint32_t value)
+{
+ switch (addr) {
+ case K230_GSDMA_GDMA_CTRL:
+ case K230_GSDMA_GDMA_LLI_BASE:
+ case K230_GSDMA_GDMA_CURRENT_LLT:
+ case K230_GSDMA_GDMA_CH_CNT0 ... K230_GSDMA_GDMA_CH_CNT_LAST:
+ return;
+ default:
+ return;
+ }
+}
+
+static void k230_gsdma_write_channel(K230GSDMAState *s, unsigned int ch,
+ hwaddr ch_off, uint32_t value)
+{
+ K230GSDMAChannel *c = &s->channels[ch];
+ bool handshake_mode = ch < 2 && k230_gsdma_decomp_enabled(s);
+
+ switch (ch_off) {
+ case K230_GSDMA_CH_CTL:
+ c->ctl = value;
+ if ((value & K230_GSDMA_CTL_STOP) != 0) {
+ c->started = false;
+ k230_gsdma_sdma_set_idle(c);
+ break;
+ }
+ if ((value & K230_GSDMA_CTL_RESUME) != 0 &&
+ (c->status & K230_GSDMA_SDMA_STATUS_PAUSE) && c->next_llt != 0) {
+ k230_gsdma_run_sdma(s, ch, c->next_llt);
+ break;
+ }
+ if (handshake_mode && (value & K230_GSDMA_CTL_START) != 0) {
+ c->started = true;
+ c->status &= ~(K230_GSDMA_SDMA_STATUS_BUSY |
+ K230_GSDMA_SDMA_STATUS_PAUSE);
+ c->current_llt = 0;
+ c->next_llt = 0;
+ break;
+ }
+ if ((value & K230_GSDMA_CTL_START) != 0 &&
+ (s->dma_ch_en & BIT(ch)) && c->llt_saddr != 0) {
+ c->started = true;
+ k230_gsdma_run_sdma(s, ch, c->llt_saddr);
+ }
+ break;
+ case K230_GSDMA_CH_CFG:
+ c->cfg = value;
+ break;
+ case K230_GSDMA_CH_USR_DATA:
+ c->usr_data = value;
+ break;
+ case K230_GSDMA_CH_LLT_SADDR:
+ c->llt_saddr = value;
+ break;
+ case K230_GSDMA_CH_CURRENT_LLT:
+ break;
+ default:
+ break;
+ }
+}
+
+static void k230_gsdma_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned int size)
+{
+ K230GSDMAState *s = K230_GSDMA(opaque);
+ unsigned int ch;
+ hwaddr ch_off;
+ uint32_t v = value;
+
+ trace_k230_gsdma_write(addr, size, value);
+
+ switch (addr) {
+ case K230_GSDMA_DMA_CH_EN:
+ s->dma_ch_en = v & K230_GSDMA_DMA_CH_EN_MASK;
+ return;
+ case K230_GSDMA_DMA_INT_MASK:
+ s->dma_int_mask = v;
+ k230_gsdma_update_irq(s);
+ return;
+ case K230_GSDMA_DMA_INT_STAT:
+ s->dma_int_stat &= ~v;
+ k230_gsdma_update_irq(s);
+ return;
+ case K230_GSDMA_DMA_CFG:
+ s->dma_cfg = v;
+ return;
+ case K230_GSDMA_DMA_WEIGHT:
+ s->dma_weight = v & 0x00ffffff;
+ return;
+ default:
+ break;
+ }
+
+ if (addr < K230_GSDMA_CH_BASE) {
+ k230_gsdma_write_gdma(s, addr, v);
+ return;
+ }
+
+ ch = (addr - K230_GSDMA_CH_BASE) / K230_GSDMA_CH_STRIDE;
+ ch_off = (addr - K230_GSDMA_CH_BASE) % K230_GSDMA_CH_STRIDE;
+ if (ch >= K230_GSDMA_NUM_SDMA_CHANNELS) {
+ return;
+ }
+
+ k230_gsdma_write_channel(s, ch, ch_off, v);
+}
+
+static void k230_gsdma_handle_signal(void *opaque, int n, int level)
+{
+ K230GSDMAState *s = opaque;
+ K230GSDMAChannel *c;
+ hwaddr addr;
+ unsigned int ch;
+
+ if (!level || n == K230_GSDMA_GPIO_DECOMP_CTRL_EN) {
+ return;
+ }
+
+ ch = n - 1;
+
+ c = &s->channels[ch];
+ if (!k230_gsdma_decomp_enabled(s) || !(s->dma_ch_en & BIT(ch)) || !c->started) {
+ return;
+ }
+
+ if (c->current_llt == 0) {
+ addr = c->llt_saddr;
+ } else {
+ addr = c->next_llt;
+ }
+ if (!addr) {
+ return;
+ }
+
+ k230_gsdma_step_sdma(s, ch, addr);
+ qemu_set_irq(s->handshake_out[n - 1], 1);
+ qemu_set_irq(s->handshake_out[n - 1], 0);
+}
+
+static const MemoryRegionOps k230_gsdma_ops = {
+ .read = k230_gsdma_read,
+ .write = k230_gsdma_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static void k230_gsdma_reset(DeviceState *dev)
+{
+ K230GSDMAState *s = K230_GSDMA(dev);
+
+ memset(s->channels, 0, sizeof(s->channels));
+ s->dma_ch_en = 0;
+ s->dma_int_mask = 0;
+ s->dma_int_stat = 0;
+ s->dma_cfg = K230_GSDMA_DMA_CFG_RESET;
+ s->dma_weight = 0;
+}
+
+static void k230_gsdma_realize(DeviceState *dev, Error **errp)
+{
+ K230GSDMAState *s = K230_GSDMA(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &k230_gsdma_ops, s,
+ TYPE_K230_GSDMA, K230_GSDMA_MMIO_SIZE);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+ qdev_init_gpio_in(DEVICE(dev), k230_gsdma_handle_signal,
+ K230_GSDMA_NUM_GPIOS_IN);
+ qdev_init_gpio_out(DEVICE(dev), s->handshake_out,
+ K230_GSDMA_NUM_GPIOS_OUT);
+}
+
+static void k230_gsdma_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = k230_gsdma_realize;
+ device_class_set_legacy_reset(dc, k230_gsdma_reset);
+ dc->vmsd = &vmstate_k230_gsdma;
+ dc->desc = "Kendryte K230 GSDMA";
+}
+
+static const TypeInfo k230_gsdma_info = {
+ .name = TYPE_K230_GSDMA,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(K230GSDMAState),
+ .class_init = k230_gsdma_class_init,
+};
+
+static void k230_gsdma_register_types(void)
+{
+ type_register_static(&k230_gsdma_info);
+}
+
+type_init(k230_gsdma_register_types)
diff --git a/hw/dma/meson.build b/hw/dma/meson.build
index cc7810beb8..45db5e7357 100644
--- a/hw/dma/meson.build
+++ b/hw/dma/meson.build
@@ -12,3 +12,4 @@ system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dma.c', 'soc_dma.c'))
system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c'))
system_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c'))
system_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c'))
+system_ss.add(when: 'CONFIG_K230_GSDMA', if_true: files('k230_gsdma.c'))
diff --git a/hw/dma/trace-events b/hw/dma/trace-events
index 4c09790f9a..a494755b5a 100644
--- a/hw/dma/trace-events
+++ b/hw/dma/trace-events
@@ -47,3 +47,9 @@ pl330_iomem_read(uint32_t addr, uint32_t data) "addr: 0x%08"PRIx32" data: 0x%08"
# xilinx_axidma.c
xilinx_axidma_loading_desc_fail(uint32_t res) "error:%u"
+
+# k230_gsdma.c
+k230_gsdma_read(uint64_t addr, unsigned int size, uint64_t data) "K230 GSDMA read: [0x%"PRIx64"] size %u -> 0x%"PRIx64
+k230_gsdma_write(uint64_t addr, unsigned int size, uint64_t data) "K230 GSDMA write: [0x%"PRIx64"] size %u <- 0x%"PRIx64
+k230_gsdma_read_sdma_llt(uint64_t addr, uint32_t cfg, uint32_t src_addr, uint32_t line_size, uint32_t line_cfg, uint32_t dst_addr, uint32_t next_llt_addr) "K230 GSDMA read SDMA LLT: addr=0x%"PRIx64" cfg=0x%"PRIx32" src=0x%"PRIx32" line_size=0x%"PRIx32" line_cfg=0x%"PRIx32" dst=0x%"PRIx32" next=0x%"PRIx32
+k230_gsdma_read_sdma_llt_failed(uint64_t addr) "K230 GSDMA read SDMA LLT failed: addr=0x%"PRIx64
diff --git a/include/hw/dma/k230_gsdma.h b/include/hw/dma/k230_gsdma.h
new file mode 100644
index 0000000000..58aaa88674
--- /dev/null
+++ b/include/hw/dma/k230_gsdma.h
@@ -0,0 +1,128 @@
+/*
+ * K230 GSDMA
+ *
+ * Copyright (c) 2026 Tao Ding <dingtao0430@163.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_DMA_K230_GSDMA_H
+#define HW_DMA_K230_GSDMA_H
+
+#include "hw/core/sysbus.h"
+
+#define TYPE_K230_GSDMA "riscv.k230.gsdma"
+OBJECT_DECLARE_SIMPLE_TYPE(K230GSDMAState, K230_GSDMA)
+
+#define K230_GSDMA_MMIO_SIZE 0x4000
+#define K230_GSDMA_NUM_SDMA_CHANNELS 4
+
+#define K230_GSDMA_CH_STRIDE 0x30
+#define K230_GSDMA_CH_BASE 0x50
+
+/* K230 SDMA request input */
+enum {
+ K230_GSDMA_GPIO_DECOMP_CTRL_EN,
+ K230_GSDMA_GPIO_DMA_WRITE_REQ,
+ K230_GSDMA_GPIO_DMA_READ_REQ,
+ K230_GSDMA_NUM_GPIOS_IN,
+};
+
+/* K230 SDMA ACK output */
+enum {
+ K230_GSDMA_GPIO_DMA_WRITE_ACK,
+ K230_GSDMA_GPIO_DMA_READ_ACK,
+ K230_GSDMA_NUM_GPIOS_OUT,
+};
+
+/* K230 GSDMA Registers Map */
+#define K230_GSDMA_DMA_CH_EN 0x00
+#define K230_GSDMA_DMA_INT_MASK 0x04
+#define K230_GSDMA_DMA_INT_STAT 0x08
+#define K230_GSDMA_DMA_CFG 0x0c
+#define K230_GSDMA_GDMA_CTRL 0x10
+#define K230_GSDMA_GDMA_LLI_BASE 0x14
+#define K230_GSDMA_GDMA_CH_CNT0 0x18
+#define K230_GSDMA_GDMA_CH_CNT_LAST 0x34
+#define K230_GSDMA_GDMA_CURRENT_LLT 0x38
+#define K230_GSDMA_DMA_WEIGHT 0x48
+
+/* K230 GSDMA SDMA Channel Register Map */
+#define K230_GSDMA_CH_CTL 0x00
+#define K230_GSDMA_CH_STATUS 0x04
+#define K230_GSDMA_CH_CFG 0x08
+#define K230_GSDMA_CH_USR_DATA 0x0c
+#define K230_GSDMA_CH_LLT_SADDR 0x10
+#define K230_GSDMA_CH_CURRENT_LLT 0x14
+
+/* K230 SDMA Control bit */
+#define K230_GSDMA_CTL_START BIT(0)
+#define K230_GSDMA_CTL_STOP BIT(1)
+#define K230_GSDMA_CTL_RESUME BIT(2)
+
+/* K230 SDMA States bit */
+#define K230_GSDMA_SDMA_STATUS_BUSY BIT(0)
+#define K230_GSDMA_SDMA_STATUS_PAUSE BIT(1)
+
+/* K230 SDMA Interrupt bit */
+#define K230_GSDMA_SDMA_DONE_INT(ch) BIT(ch)
+#define K230_GSDMA_SDMA_ITEM_INT(ch) BIT((ch) + 4)
+#define K230_GSDMA_SDMA_PAUSE_INT(ch) BIT((ch) + 8)
+
+
+#define K230_GSDMA_DMA_CH_EN_MASK MAKE_64BIT_MASK(0, 5)
+#define K230_GSDMA_DMA_CFG_RESET 0x000007ff
+
+/* K230 SDMA Linked List bit */
+#define K230_GSDMA_LLT_2D_MODE BIT(28)
+#define K230_GSDMA_LLT_PAUSE BIT(29)
+#define K230_GSDMA_LLT_NODE_INTR BIT(30)
+#define K230_GSDMA_GDMA_LLT_GCH_SHIFT 16
+#define K230_GSDMA_GDMA_LLT_GCH_MASK 0x7
+#define K230_GSDMA_CH_CFG_DAT_MODE BIT(0)
+#define K230_GSDMA_CH_CFG_USR_DATA_SIZE_SHIFT 1
+#define K230_GSDMA_CH_CFG_USR_DATA_SIZE_MASK 0x3
+#define K230_GSDMA_CH_CFG_SRC_FIXED BIT(8)
+#define K230_GSDMA_CH_CFG_DST_FIXED BIT(9)
+#define K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN BIT(10)
+
+/* K230 SDMA channel state */
+typedef struct K230GSDMAChannel {
+ uint32_t ctl;
+ uint32_t status;
+ uint32_t cfg;
+ uint32_t usr_data;
+ uint32_t llt_saddr;
+ uint32_t current_llt;
+ uint32_t next_llt;
+ bool started;
+} K230GSDMAChannel;
+
+/* K230 SDMA Linked List */
+typedef struct K230GSDMALLT {
+ uint32_t cfg;
+ uint32_t src_addr;
+ uint32_t line_size;
+ uint32_t line_cfg;
+ uint32_t dst_addr;
+ uint32_t next_llt_addr;
+} K230GSDMALLT;
+
+/* K230 GSDMA state */
+struct K230GSDMAState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ qemu_irq irq;
+ qemu_irq handshake_out[K230_GSDMA_NUM_GPIOS_OUT];
+
+ uint32_t dma_ch_en;
+ uint32_t dma_int_mask;
+ uint32_t dma_int_stat;
+ uint32_t dma_cfg;
+ uint32_t dma_weight;
+
+ K230GSDMAChannel channels[K230_GSDMA_NUM_SDMA_CHANNELS];
+};
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 2/7] hw/riscv: k230: add gsdma in K230 board
2026-07-21 16:13 [PATCH 0/7] K230: add gsdma and decomp_gzip Tao Ding
2026-07-21 16:13 ` [PATCH 1/7] hw/dma: add K230 gsdma Tao Ding
@ 2026-07-21 16:13 ` Tao Ding
2026-07-21 16:13 ` [PATCH 3/7] tests/qtest: add test for K230 gsdma Tao Ding
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Tao Ding @ 2026-07-21 16:13 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-riscv, pbonzini, palmer, alistair.francis, liwei1518,
daniel.barboza, zhiwei_liu, chao.liu, farosas, lvivier, Tao Ding
This commit replaces the unimplemented part of GSDMA in K230. And connect the interrupt to plic.
Signed-off-by: Tao Ding <dingtao0430@163.com>
---
hw/riscv/Kconfig | 1 +
hw/riscv/k230.c | 12 +++++++++---
include/hw/riscv/k230.h | 3 +++
3 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index de37c08cae..d28ac06043 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -162,3 +162,4 @@ config K230
select SERIAL_MM
select UNIMP
select K230_WDT
+ select K230_GSDMA
diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c
index 656f28190c..c80bd65694 100644
--- a/hw/riscv/k230.c
+++ b/hw/riscv/k230.c
@@ -110,6 +110,7 @@ static void k230_soc_init(Object *obj)
object_initialize_child(obj, "c908-cpu", cpu0, TYPE_RISCV_HART_ARRAY);
object_initialize_child(obj, "k230-wdt0", &s->wdt[0], TYPE_K230_WDT);
object_initialize_child(obj, "k230-wdt1", &s->wdt[1], TYPE_K230_WDT);
+ object_initialize_child(obj, "k230-gsdma", &s->gsdma, TYPE_K230_GSDMA);
qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0);
qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C908);
@@ -206,6 +207,14 @@ static void k230_soc_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[1]), 0,
qdev_get_gpio_in(DEVICE(s->c908_plic), K230_WDT1_IRQ));
+ /* Gsdma */
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gsdma), errp)) {
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gsdma), 0, memmap[K230_DEV_GSDMA].base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gsdma), 0,
+ qdev_get_gpio_in(DEVICE(s->c908_plic), K230_GSDMA_IRQ));
+
/* unimplemented devices */
create_unimplemented_device("kpu.l2-cache",
memmap[K230_DEV_KPU_L2_CACHE].base,
@@ -221,9 +230,6 @@ static void k230_soc_realize(DeviceState *dev, Error **errp)
memmap[K230_DEV_AI_2D_ENGINE].base,
memmap[K230_DEV_AI_2D_ENGINE].size);
- create_unimplemented_device("gsdma", memmap[K230_DEV_GSDMA].base,
- memmap[K230_DEV_GSDMA].size);
-
create_unimplemented_device("dma", memmap[K230_DEV_DMA].base,
memmap[K230_DEV_DMA].size);
diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h
index 592e1c26bf..156a386edc 100644
--- a/include/hw/riscv/k230.h
+++ b/include/hw/riscv/k230.h
@@ -18,6 +18,7 @@
#include "hw/core/boards.h"
#include "hw/riscv/riscv_hart.h"
#include "hw/watchdog/k230_wdt.h"
+#include "hw/dma/k230_gsdma.h"
#define C908_CPU_HARTID (0)
@@ -33,6 +34,7 @@ typedef struct K230SoCState {
RISCVHartArrayState c908_cpu; /* Small core */
K230WdtState wdt[2];
+ K230GSDMAState gsdma;
MemoryRegion sram;
MemoryRegion bootrom;
@@ -129,6 +131,7 @@ enum {
K230_UART4_IRQ = 20,
K230_WDT0_IRQ = 107,
K230_WDT1_IRQ = 108,
+ K230_GSDMA_IRQ = 140,
};
#define K230_UART_COUNT 5
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 3/7] tests/qtest: add test for K230 gsdma
2026-07-21 16:13 [PATCH 0/7] K230: add gsdma and decomp_gzip Tao Ding
2026-07-21 16:13 ` [PATCH 1/7] hw/dma: add K230 gsdma Tao Ding
2026-07-21 16:13 ` [PATCH 2/7] hw/riscv: k230: add gsdma in K230 board Tao Ding
@ 2026-07-21 16:13 ` Tao Ding
2026-07-21 16:13 ` [PATCH 4/7] hw/misc: add K230 decomp gzip Tao Ding
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Tao Ding @ 2026-07-21 16:13 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-riscv, pbonzini, palmer, alistair.francis, liwei1518,
daniel.barboza, zhiwei_liu, chao.liu, farosas, lvivier, Tao Ding
This commit adds test cases for GSDMA.
Imitate the behavior of the driver, set LLT and sdma registers.
After the data transmission is completed, check the destination address data.
Run this qtest:
$ mkdir build && cd build && ../configure --target-list="riscv64-softmmu"
$ QTEST_QEMU_BINARY=./qemu-system-riscv64 tests/qtest/k230-gsdma-test
Signed-off-by: Tao Ding <dingtao0430@163.com>
---
tests/qtest/k230-gsdma-test.c | 201 ++++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 2 +-
2 files changed, 202 insertions(+), 1 deletion(-)
create mode 100644 tests/qtest/k230-gsdma-test.c
diff --git a/tests/qtest/k230-gsdma-test.c b/tests/qtest/k230-gsdma-test.c
new file mode 100644
index 0000000000..44d4248739
--- /dev/null
+++ b/tests/qtest/k230-gsdma-test.c
@@ -0,0 +1,201 @@
+/*
+ * QTest testcase for K230 GSDMA
+ *
+ * Copyright (c) 2026 Tao Ding <dingtao0430@163.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "exec/hwaddr.h"
+#include "hw/dma/k230_gsdma.h"
+#include "libqtest.h"
+
+#define K230_GSDMA_BASE 0x80800000
+#define TEST_LLT_ADDR0 0x10000
+#define TEST_LLT_ADDR1 0x10040
+#define TEST_LLT_ADDR2 0x10080
+
+#define TEST_SRC_ADDR0 0x20000
+#define TEST_SRC_ADDR1 0x22000
+#define TEST_SRC_ADDR2 0x23000
+#define TEST_DST_ADDR0 0x30000
+#define TEST_DST_ADDR1 0x32000
+#define TEST_DST_ADDR2 0x33000
+
+static inline uint64_t gsdma_reg(hwaddr off)
+{
+ return K230_GSDMA_BASE + off;
+}
+
+static inline uint64_t gsdma_ch_reg(unsigned int ch, hwaddr off)
+{
+ return K230_GSDMA_BASE + K230_GSDMA_CH_BASE +
+ ch * K230_GSDMA_CH_STRIDE + off;
+}
+
+static void write_sdma_llt_node_full(QTestState *qts, hwaddr addr,
+ uint32_t cfg, hwaddr src,
+ uint32_t size, hwaddr dst,
+ hwaddr next)
+{
+ qtest_writel(qts, addr + offsetof(K230GSDMALLT, cfg), cfg);
+ qtest_writel(qts, addr + offsetof(K230GSDMALLT, src_addr), src);
+ qtest_writel(qts, addr + offsetof(K230GSDMALLT, line_size), size);
+ qtest_writel(qts, addr + offsetof(K230GSDMALLT, line_cfg), 0x1);
+ qtest_writel(qts, addr + offsetof(K230GSDMALLT, dst_addr), dst);
+ qtest_writel(qts, addr + offsetof(K230GSDMALLT, next_llt_addr), next);
+}
+
+static void write_sdma_llt_node(QTestState *qts, hwaddr addr, uint32_t cfg,
+ uint32_t next)
+{
+ write_sdma_llt_node_full(qts, addr, cfg, 0x11110000, 0x200,
+ 0x22220000, next);
+}
+
+static void fill_pattern(uint8_t *buf, size_t size, uint8_t seed)
+{
+ for (size_t i = 0; i < size; i++) {
+ buf[i] = seed + i * 37;
+ }
+}
+
+static void test_global_registers(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_CFG)), ==,
+ K230_GSDMA_DMA_CFG_RESET);
+
+ qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN), 0xff);
+ g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN)), ==,
+ K230_GSDMA_DMA_CH_EN_MASK);
+
+ qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_INT_MASK), 0x12345);
+ g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_MASK)), ==,
+ 0x12345);
+
+ qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CFG), 0xa5a5a5a5);
+ g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_CFG)), ==,
+ 0xa5a5a5a5);
+
+ qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_WEIGHT), 0xff55aa55);
+ g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_WEIGHT)), ==,
+ 0x55aa55);
+
+ qtest_quit(qts);
+}
+
+static void test_sdma_pause_resume(void)
+{
+ uint32_t int_stat;
+ QTestState *qts = qtest_init("-machine k230");
+
+ write_sdma_llt_node(qts, TEST_LLT_ADDR0, K230_GSDMA_LLT_PAUSE,
+ TEST_LLT_ADDR1);
+ write_sdma_llt_node(qts, TEST_LLT_ADDR1, K230_GSDMA_LLT_NODE_INTR, 0);
+
+ qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN), BIT(0));
+ qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_LLT_SADDR), TEST_LLT_ADDR0);
+ qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_START);
+
+ g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_STATUS)), ==,
+ K230_GSDMA_SDMA_STATUS_PAUSE);
+ g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CURRENT_LLT)),
+ ==, TEST_LLT_ADDR0);
+
+ int_stat = qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT));
+ g_assert_cmphex(int_stat & K230_GSDMA_SDMA_PAUSE_INT(0), ==,
+ K230_GSDMA_SDMA_PAUSE_INT(0));
+ g_assert_cmphex(int_stat & K230_GSDMA_SDMA_DONE_INT(0), ==, 0);
+
+ qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT), int_stat);
+ qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_RESUME);
+
+ g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_STATUS)), ==,
+ 0);
+ g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CURRENT_LLT)),
+ ==, TEST_LLT_ADDR1);
+
+ int_stat = qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT));
+ g_assert_cmphex(int_stat & K230_GSDMA_SDMA_DONE_INT(0), ==,
+ K230_GSDMA_SDMA_DONE_INT(0));
+ g_assert_cmphex(int_stat & K230_GSDMA_SDMA_ITEM_INT(0), ==,
+ K230_GSDMA_SDMA_ITEM_INT(0));
+
+ qtest_quit(qts);
+}
+
+static void test_sdma_three_llt_copy(void)
+{
+ uint8_t src0[0x1000];
+ uint8_t src1[1];
+ uint8_t src2[511];
+ uint8_t dst0[sizeof(src0)];
+ uint8_t dst1[sizeof(src1)];
+ uint8_t dst2[sizeof(src2)];
+ uint32_t int_stat;
+ QTestState *qts = qtest_init("-machine k230");
+
+ fill_pattern(src0, sizeof(src0), 0x10);
+ fill_pattern(src1, sizeof(src1), 0x31);
+ fill_pattern(src2, sizeof(src2), 0x52);
+ memset(dst0, 0xa5, sizeof(dst0));
+ memset(dst1, 0xa5, sizeof(dst1));
+ memset(dst2, 0xa5, sizeof(dst2));
+
+ qtest_memwrite(qts, TEST_SRC_ADDR0, src0, sizeof(src0));
+ qtest_memwrite(qts, TEST_SRC_ADDR1, src1, sizeof(src1));
+ qtest_memwrite(qts, TEST_SRC_ADDR2, src2, sizeof(src2));
+ qtest_memwrite(qts, TEST_DST_ADDR0, dst0, sizeof(dst0));
+ qtest_memwrite(qts, TEST_DST_ADDR1, dst1, sizeof(dst1));
+ qtest_memwrite(qts, TEST_DST_ADDR2, dst2, sizeof(dst2));
+
+ write_sdma_llt_node_full(qts, TEST_LLT_ADDR0, 0, TEST_SRC_ADDR0,
+ sizeof(src0), TEST_DST_ADDR0, TEST_LLT_ADDR1);
+ write_sdma_llt_node_full(qts, TEST_LLT_ADDR1, 0, TEST_SRC_ADDR1,
+ sizeof(src1), TEST_DST_ADDR1, TEST_LLT_ADDR2);
+ write_sdma_llt_node_full(qts, TEST_LLT_ADDR2, 0, TEST_SRC_ADDR2,
+ sizeof(src2), TEST_DST_ADDR2, 0);
+
+ qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN), BIT(0));
+ qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_LLT_SADDR), TEST_LLT_ADDR0);
+ qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_START);
+
+ g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_STATUS)), ==,
+ 0);
+ g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CURRENT_LLT)),
+ ==, TEST_LLT_ADDR2);
+
+ int_stat = qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT));
+ g_assert_cmphex(int_stat & K230_GSDMA_SDMA_DONE_INT(0), ==,
+ K230_GSDMA_SDMA_DONE_INT(0));
+ g_assert_cmphex(int_stat & K230_GSDMA_SDMA_PAUSE_INT(0), ==, 0);
+
+ memset(dst0, 0, sizeof(dst0));
+ memset(dst1, 0, sizeof(dst1));
+ memset(dst2, 0, sizeof(dst2));
+ qtest_memread(qts, TEST_DST_ADDR0, dst0, sizeof(dst0));
+ qtest_memread(qts, TEST_DST_ADDR1, dst1, sizeof(dst1));
+ qtest_memread(qts, TEST_DST_ADDR2, dst2, sizeof(dst2));
+
+ g_assert_cmpmem(dst0, sizeof(dst0), src0, sizeof(src0));
+ g_assert_cmpmem(dst1, sizeof(dst1), src1, sizeof(src1));
+ g_assert_cmpmem(dst2, sizeof(dst2), src2, sizeof(src2));
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/k230-gsdma/global-registers", test_global_registers);
+ qtest_add_func("/k230-gsdma/sdma-pause-resume", test_sdma_pause_resume);
+ qtest_add_func("/k230-gsdma/sdma-three-llt-copy",
+ test_sdma_three_llt_copy);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 56ff860e21..1b13a777c9 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -297,7 +297,7 @@ qtests_riscv64 = ['riscv-csr-test'] + \
(config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and
config_all_devices.has_key('CONFIG_RISCV_IOMMU') ?
['iommu-riscv-test'] : []) + \
- (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : [])
+ (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test', 'k230-gsdma-test'] : [])
qtests_hexagon = ['boot-serial-test']
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 4/7] hw/misc: add K230 decomp gzip
2026-07-21 16:13 [PATCH 0/7] K230: add gsdma and decomp_gzip Tao Ding
` (2 preceding siblings ...)
2026-07-21 16:13 ` [PATCH 3/7] tests/qtest: add test for K230 gsdma Tao Ding
@ 2026-07-21 16:13 ` Tao Ding
2026-07-21 16:13 ` [PATCH 5/7] hw/riscv: k230: add decomp gzip in K230 board Tao Ding
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Tao Ding @ 2026-07-21 16:13 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-riscv, pbonzini, palmer, alistair.francis, liwei1518,
daniel.barboza, zhiwei_liu, chao.liu, farosas, lvivier, Tao Ding
Decompression accelerator is mainly used to implement hardware GZIP decompression function.
According to the k230 SDK, this hardware will be used during the uboot phase to decompress
compressed files.
Decomp_gzip does not have the ability to access the system bus.
Therefore, SDMA is required for data transmission. A typical scenario is SDMA moving compressed
data from DDR to SRAM, and Decomp_gzip reading data from SRAM. After decompression, decomp_gzip
writes the data back to SRAM, and SDMA moves the data from SRAM to DDR.
According to "K230 Technical Reference Manual v0.3.1" section 15.
https://download.kendryte.com/developer/k230/HDK/K230%E7%A1%AC%E4%BB%B6%E6%96%87%E6%A1%A3/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf
Signed-off-by: Tao Ding <dingtao0430@163.com>
---
hw/misc/Kconfig | 3 +
hw/misc/k230_decomp_gzip.c | 498 +++++++++++++++++++++++++++++
hw/misc/meson.build | 1 +
hw/misc/trace-events | 4 +
include/hw/misc/k230_decomp_gzip.h | 92 ++++++
5 files changed, 598 insertions(+)
create mode 100644 hw/misc/k230_decomp_gzip.c
create mode 100644 include/hw/misc/k230_decomp_gzip.h
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 1543ee6653..28687482f8 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -257,4 +257,7 @@ config XLNX_VERSAL_TRNG
config XLNX_ZYNQ_DDRC
bool
+config K230_DECOMP_GZIP
+ bool
+
source macio/Kconfig
diff --git a/hw/misc/k230_decomp_gzip.c b/hw/misc/k230_decomp_gzip.c
new file mode 100644
index 0000000000..4284262259
--- /dev/null
+++ b/hw/misc/k230_decomp_gzip.c
@@ -0,0 +1,498 @@
+/*
+ * Kendryte K230 GZIP decompression engine
+ *
+ * Copyright (c) 2026 Tao Ding <dingtao0430@163.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Decompression accelerator is mainly used to implement hardware
+ * GZIP decompression function. (K230 TRM section 15)
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/core/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "system/address-spaces.h"
+#include "hw/core/irq.h"
+#include "hw/misc/k230_decomp_gzip.h"
+#include "trace.h"
+
+#define GZIP_METHOD_DEFLATE 8
+#define GZIP_METHOD_VENDOR_9 9
+
+
+#define K230_DECOMP_GZIP_OUTPUT_SLOTS 4
+#define K230_DECOMP_GZIP_INPUT_SLOTS 2
+
+static void k230_decomp_gzip_set_output(K230DecompGzipState *s, int line,
+ int level)
+{
+ qemu_set_irq(s->signal_out[line], level);
+}
+
+static bool k230_decomp_gzip_read_mem(K230DecompGzipState *s, hwaddr addr,
+ void *buf, size_t len)
+{
+ MemTxResult ret = address_space_read(&address_space_memory,
+ s->sram_base + addr,
+ MEMTXATTRS_UNSPECIFIED, buf, len);
+
+ return ret == MEMTX_OK;
+}
+
+static bool k230_decomp_gzip_write_mem(K230DecompGzipState *s, hwaddr addr,
+ const void *buf, size_t len)
+{
+ return address_space_write(&address_space_memory, s->sram_base + addr,
+ MEMTXATTRS_UNSPECIFIED, buf, len) == MEMTX_OK;
+}
+
+static hwaddr k230_decomp_gzip_output_addr(K230DecompGzipState *s)
+{
+ return K230_DECOMP_GZIP_SRAM_OUT_BASE +
+ s->output.slot * K230_DECOMP_GZIP_BLOCK_SIZE +
+ s->output.current_offset;
+}
+
+static hwaddr k230_decomp_gzip_input_addr(K230DecompGzipState *s)
+{
+ return s->input.slot * K230_DECOMP_GZIP_BLOCK_SIZE +
+ K230_DECOMP_GZIP_SRAM_IN_BASE;
+}
+
+static uint32_t k230_decomp_gzip_current_input_size(K230DecompGzipState *s)
+{
+ if (s->total_requested == 0) {
+ return 0;
+ }
+
+ return ((s->total_requested - 1) % K230_DECOMP_GZIP_BLOCK_SIZE) + 1;
+}
+
+static void k230_decomp_gzip_update_ctrl_en(K230DecompGzipState *s)
+{
+ int level = s->active && !!(s->gzip_src_size & K230_DECOMP_GZIP_CTRL_EN);
+
+ k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DECOMP_CTRL_EN,
+ level);
+}
+
+static void k230_decomp_gzip_reset_stream(K230DecompGzipState *s)
+{
+ if (s->zstream_inited) {
+ inflateEnd(&s->zs);
+ s->zstream_inited = false;
+ }
+ memset(&s->zs, 0, sizeof(s->zs));
+}
+
+static void k230_decomp_gzip_finish(K230DecompGzipState *s, bool crc_ok)
+{
+ s->active = false;
+ k230_decomp_gzip_reset_stream(s);
+ k230_decomp_gzip_update_ctrl_en(s);
+ if (crc_ok) {
+ s->decomp_stat |= K230_DECOMP_GZIP_STAT_CRC_OK;
+ } else {
+ s->decomp_stat &= ~K230_DECOMP_GZIP_STAT_CRC_OK;
+ }
+}
+
+static bool k230_decomp_gzip_load_input(K230DecompGzipState *s,
+ uint32_t addr, uint32_t size)
+{
+ s->input.current_offset = 0;
+
+ return k230_decomp_gzip_read_mem(s, addr, s->input_buf, size);
+}
+
+static bool k230_decomp_gzip_request_input(K230DecompGzipState *s)
+{
+ uint32_t total_requested = s->total_requested;
+
+ k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DMA_WRITE_REQ, 1);
+ if (s->total_requested == total_requested) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: dma write request not acknowledged\n",
+ TYPE_K230_DECOMP_GZIP);
+ return false;
+ }
+ return true;
+}
+
+static bool k230_decomp_gzip_request_output(K230DecompGzipState *s)
+{
+ uint32_t slot = s->output.slot;
+ uint32_t current_offset = s->output.current_offset;
+
+ k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DMA_READ_REQ, 1);
+ if (s->output.slot == slot &&
+ s->output.current_offset == current_offset) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: dma read request not acknowledged\n",
+ TYPE_K230_DECOMP_GZIP);
+ return false;
+ }
+ return true;
+}
+
+static bool k230_decomp_gzip_init_stream(K230DecompGzipState *s)
+{
+ /* The compression script for K230 will modify the third byte to 0x9 */
+ if (s->input_buf[2] == GZIP_METHOD_VENDOR_9) {
+ s->input_buf[2] = GZIP_METHOD_DEFLATE;
+ }
+
+ if (inflateInit2(&s->zs, 15 + 16) != Z_OK) {
+ return false;
+ }
+
+ s->zstream_inited = true;
+ return true;
+}
+
+static bool k230_decomp_gzip_run_inflate(K230DecompGzipState *s)
+{
+ uint32_t output_size = s->gzip_out_size;
+ uint32_t current_input_size = k230_decomp_gzip_current_input_size(s);
+ uint32_t avail_in;
+ uint32_t avail_out;
+ uint32_t consumed;
+ uint32_t produced;
+ int ret;
+
+ avail_in = current_input_size - s->input.current_offset;
+ avail_out = K230_DECOMP_GZIP_BLOCK_SIZE - s->output.current_offset;
+
+ s->zs.next_in = s->input_buf + s->input.current_offset;
+ s->zs.avail_in = avail_in;
+ s->zs.next_out = s->output_buf;
+ s->zs.avail_out = avail_out;
+
+ ret = inflate(&s->zs, Z_NO_FLUSH);
+ if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) {
+ return false;
+ }
+
+ consumed = avail_in - s->zs.avail_in;
+ produced = avail_out - s->zs.avail_out;
+
+ if (produced) {
+ if (s->total_produced + produced > output_size ||
+ !k230_decomp_gzip_write_mem(s, k230_decomp_gzip_output_addr(s),
+ s->output_buf, produced)) {
+ return false;
+ }
+ s->output.current_offset += produced;
+ s->total_produced += produced;
+ }
+
+ if (consumed) {
+ s->input.current_offset += consumed;
+ }
+
+ if (ret == Z_STREAM_END) {
+ s->stream_end = true;
+ if (s->total_produced != output_size) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void k230_decomp_gzip_kick(K230DecompGzipState *s)
+{
+ if (!s->active || s->in_kick) {
+ return;
+ }
+
+ uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK;
+ s->in_kick = true;
+ while (s->active) {
+ uint32_t current_input_size = k230_decomp_gzip_current_input_size(s);
+ /* Output is full or last block */
+ if (s->output.current_offset == K230_DECOMP_GZIP_BLOCK_SIZE ||
+ (s->stream_end && s->output.current_offset != 0)) {
+ if (!k230_decomp_gzip_request_output(s)) {
+ k230_decomp_gzip_finish(s, false);
+ break;
+ }
+ continue;
+ }
+
+ if (!s->zstream_inited) {
+ if (!k230_decomp_gzip_request_input(s) ||
+ !k230_decomp_gzip_init_stream(s)) {
+ k230_decomp_gzip_finish(s, false);
+ break;
+ }
+ continue;
+ }
+
+ /* Transfer finish */
+ if (s->stream_end) {
+ k230_decomp_gzip_finish(s,
+ s->total_produced == s->gzip_out_size);
+ break;
+ }
+
+ /* Input buf has has been fully decompreed, need request more */
+ if (s->input.current_offset == current_input_size) {
+ if (s->total_requested < input_size) {
+ if (!k230_decomp_gzip_request_input(s)) {
+ k230_decomp_gzip_finish(s, false);
+ break;
+ }
+ continue;
+ }
+ k230_decomp_gzip_finish(s, false);
+ break;
+ }
+
+ /* Decompress the data, input buf or output buf are consumed in one block */
+ if (!k230_decomp_gzip_run_inflate(s)) {
+ k230_decomp_gzip_finish(s, false);
+ break;
+ }
+ }
+ s->in_kick = false;
+}
+
+static void k230_decomp_gzip_handle_ack(void *opaque, int n, int level)
+{
+ K230DecompGzipState *s = opaque;
+
+ if (!level || !s->active) {
+ return;
+ }
+
+ switch (n) {
+ case K230_DECOMP_GZIP_GPIO_DMA_WRITE_ACK:
+ {
+ uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK;
+ uint32_t chunk = MIN(K230_DECOMP_GZIP_BLOCK_SIZE,
+ input_size - s->total_requested);
+
+ if (!k230_decomp_gzip_load_input(s, k230_decomp_gzip_input_addr(s),
+ chunk)) {
+ k230_decomp_gzip_finish(s, false);
+ return;
+ }
+ s->total_requested += chunk;
+ s->input.slot = (s->input.slot + 1) % K230_DECOMP_GZIP_INPUT_SLOTS;
+ break;
+ }
+ case K230_DECOMP_GZIP_GPIO_DMA_READ_ACK:
+ if (s->output.current_offset == 0) {
+ k230_decomp_gzip_finish(s, false);
+ return;
+ }
+ s->output.slot = (s->output.slot + 1) % K230_DECOMP_GZIP_OUTPUT_SLOTS;
+ s->output.current_offset = 0;
+ break;
+ default:
+ return;
+ }
+
+ k230_decomp_gzip_kick(s);
+}
+
+static void k230_decomp_gzip_start(K230DecompGzipState *s)
+{
+ uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK;
+
+ k230_decomp_gzip_reset_stream(s);
+ s->decomp_stat &= ~K230_DECOMP_GZIP_STAT_CRC_OK;
+ s->total_requested = 0;
+ s->total_produced = 0;
+ s->stream_end = false;
+ memset(&s->input, 0, sizeof(s->input));
+ memset(&s->output, 0, sizeof(s->output));
+
+ if (!(s->gzip_src_size & K230_DECOMP_GZIP_CTRL_EN) ||
+ input_size == 0 || s->gzip_out_size == 0) {
+ k230_decomp_gzip_finish(s, false);
+ return;
+ }
+
+ s->active = true;
+ k230_decomp_gzip_update_ctrl_en(s);
+ k230_decomp_gzip_kick(s);
+}
+
+static uint64_t k230_decomp_gzip_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ K230DecompGzipState *s = opaque;
+ uint64_t value = 0;
+
+ switch (offset) {
+ case K230_DECOMP_GZIP_DECOMP_START:
+ value = s->decomp_start;
+ break;
+ case K230_DECOMP_GZIP_GZIP_SRC_SIZE:
+ value = s->gzip_src_size;
+ break;
+ case K230_DECOMP_GZIP_GZIP_OUT_SIZE:
+ value = s->gzip_out_size;
+ break;
+ case K230_DECOMP_GZIP_DECOMP_STAT:
+ value = s->decomp_stat;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad read offset 0x%" HWADDR_PRIx "\n",
+ TYPE_K230_DECOMP_GZIP, offset);
+ break;
+ }
+
+ trace_k230_decomp_gzip_read(offset, size, value);
+ return value;
+}
+
+static void k230_decomp_gzip_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ K230DecompGzipState *s = opaque;
+
+ trace_k230_decomp_gzip_write(offset, size, value);
+
+ switch (offset) {
+ case K230_DECOMP_GZIP_DECOMP_START:
+ s->decomp_start = value;
+ if (value & K230_DECOMP_GZIP_START) {
+ k230_decomp_gzip_start(s);
+ }
+ break;
+ case K230_DECOMP_GZIP_GZIP_SRC_SIZE:
+ s->gzip_src_size = value;
+ k230_decomp_gzip_update_ctrl_en(s);
+ break;
+ case K230_DECOMP_GZIP_GZIP_OUT_SIZE:
+ s->gzip_out_size = value;
+ break;
+ case K230_DECOMP_GZIP_DECOMP_STAT:
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad write offset 0x%" HWADDR_PRIx "\n",
+ TYPE_K230_DECOMP_GZIP, offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps k230_decomp_gzip_ops = {
+ .read = k230_decomp_gzip_read,
+ .write = k230_decomp_gzip_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_k230_decomp_gzip_slot = {
+ .name = "k230.decomp-gzip.slot",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(slot, K230DecompGzipSlotState),
+ VMSTATE_UINT32(current_offset, K230DecompGzipSlotState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_k230_decomp_gzip = {
+ .name = "k230.decomp-gzip",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(decomp_start, K230DecompGzipState),
+ VMSTATE_UINT32(gzip_src_size, K230DecompGzipState),
+ VMSTATE_UINT32(gzip_out_size, K230DecompGzipState),
+ VMSTATE_UINT32(decomp_stat, K230DecompGzipState),
+ VMSTATE_STRUCT(input, K230DecompGzipState, 1,
+ vmstate_k230_decomp_gzip_slot,
+ K230DecompGzipSlotState),
+ VMSTATE_STRUCT(output, K230DecompGzipState, 1,
+ vmstate_k230_decomp_gzip_slot,
+ K230DecompGzipSlotState),
+ VMSTATE_UINT32(total_requested, K230DecompGzipState),
+ VMSTATE_UINT32(total_produced, K230DecompGzipState),
+ VMSTATE_BOOL(active, K230DecompGzipState),
+ VMSTATE_BOOL(in_kick, K230DecompGzipState),
+ VMSTATE_BOOL(zstream_inited, K230DecompGzipState),
+ VMSTATE_BOOL(stream_end, K230DecompGzipState),
+ VMSTATE_UINT8_ARRAY(input_buf, K230DecompGzipState,
+ K230_DECOMP_GZIP_BLOCK_SIZE),
+ VMSTATE_UINT8_ARRAY(output_buf, K230DecompGzipState,
+ K230_DECOMP_GZIP_BLOCK_SIZE),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void k230_decomp_gzip_reset(DeviceState *dev)
+{
+ K230DecompGzipState *s = K230_DECOMP_GZIP(dev);
+
+ k230_decomp_gzip_reset_stream(s);
+ memset(&s->zs, 0, sizeof(s->zs));
+ s->decomp_start = 0;
+ s->gzip_src_size = 0;
+ s->gzip_out_size = 0;
+ s->decomp_stat = 0;
+ memset(&s->input, 0, sizeof(s->input));
+ memset(&s->output, 0, sizeof(s->output));
+ s->total_requested = 0;
+ s->total_produced = 0;
+ s->active = false;
+ s->in_kick = false;
+ s->stream_end = false;
+}
+
+static void k230_decomp_gzip_realize(DeviceState *dev, Error **errp)
+{
+ K230DecompGzipState *s = K230_DECOMP_GZIP(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &k230_decomp_gzip_ops, s,
+ TYPE_K230_DECOMP_GZIP,
+ K230_DECOMP_GZIP_MMIO_SIZE);
+ sysbus_init_mmio(sbd, &s->iomem);
+ qdev_init_gpio_in(dev, k230_decomp_gzip_handle_ack,
+ K230_DECOMP_GZIP_NUM_GPIOS_IN);
+ qdev_init_gpio_out(dev, s->signal_out,
+ K230_DECOMP_GZIP_NUM_GPIOS_OUT);
+}
+
+static const Property k230_decomp_gzip_properties[] = {
+ DEFINE_PROP_UINT64("sram-base", K230DecompGzipState, sram_base, 0),
+};
+
+static void k230_decomp_gzip_class_init(ObjectClass *oc, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = k230_decomp_gzip_realize;
+ device_class_set_legacy_reset(dc, k230_decomp_gzip_reset);
+ device_class_set_props(dc, k230_decomp_gzip_properties);
+ dc->vmsd = &vmstate_k230_decomp_gzip;
+ dc->desc = "Kendryte K230 GZIP decompression engine";
+}
+
+static const TypeInfo k230_decomp_gzip_info = {
+ .name = TYPE_K230_DECOMP_GZIP,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(K230DecompGzipState),
+ .class_init = k230_decomp_gzip_class_init,
+};
+
+static void k230_decomp_gzip_register_types(void)
+{
+ type_register_static(&k230_decomp_gzip_info);
+}
+
+type_init(k230_decomp_gzip_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 23265f6035..6f6bf3c85c 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -36,6 +36,7 @@ system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c'))
system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c'))
system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c'))
system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c'))
+system_ss.add(when: 'CONFIG_K230_DECOMP_GZIP', if_true: files('k230_decomp_gzip.c'))
subdir('macio')
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index c9a868b3ef..16db4ba0cc 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -442,3 +442,7 @@ iommu_testdev_dma_read(uint64_t gva, uint32_t len) "gva=0x%" PRIx64 " len=%u"
iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=0x%x actual=0x%x"
iommu_testdev_dma_result(uint32_t result) "DMA completed result=0x%x"
iommu_testdev_dma_armed(bool armed) "armed=%d"
+
+# k230_decomp_gzip.c
+k230_decomp_gzip_read(uint64_t offset, unsigned int size, uint64_t value) "K230 DECOMP GZIP read: [0x%"PRIx64"] size %u -> 0x%"PRIx64
+k230_decomp_gzip_write(uint64_t offset, unsigned int size, uint64_t value) "K230 DECOMP GZIP write: [0x%"PRIx64"] size %u <- 0x%"PRIx64
diff --git a/include/hw/misc/k230_decomp_gzip.h b/include/hw/misc/k230_decomp_gzip.h
new file mode 100644
index 0000000000..9911af0095
--- /dev/null
+++ b/include/hw/misc/k230_decomp_gzip.h
@@ -0,0 +1,92 @@
+/*
+ * K230 Decompress Engine
+ *
+ * Copyright (c) 2026 Tao Ding <dingtao0430@163.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MISC_K230_DECOMP_GZIP_H
+#define HW_MISC_K230_DECOMP_GZIP_H
+
+#include <zlib.h>
+#include "hw/core/sysbus.h"
+
+#define TYPE_K230_DECOMP_GZIP "riscv.k230.decomp-gzip"
+OBJECT_DECLARE_SIMPLE_TYPE(K230DecompGzipState, K230_DECOMP_GZIP)
+
+/* K230 DECOMP GZIP ACK input */
+enum {
+ K230_DECOMP_GZIP_GPIO_DMA_WRITE_ACK,
+ K230_DECOMP_GZIP_GPIO_DMA_READ_ACK,
+ K230_DECOMP_GZIP_NUM_GPIOS_IN,
+};
+
+/* K230 DECOMP GZIP REQ output */
+enum {
+ K230_DECOMP_GZIP_GPIO_DECOMP_CTRL_EN,
+ K230_DECOMP_GZIP_GPIO_DMA_WRITE_REQ,
+ K230_DECOMP_GZIP_GPIO_DMA_READ_REQ,
+ K230_DECOMP_GZIP_NUM_GPIOS_OUT,
+};
+
+#define K230_DECOMP_GZIP_MMIO_SIZE 0x4000
+
+/* K230 DECOMP GZIP Registers map */
+/* Start decompression controller */
+#define K230_DECOMP_GZIP_DECOMP_START 0x00
+/* Source data length register */
+#define K230_DECOMP_GZIP_GZIP_SRC_SIZE 0x04
+/* Output data length register */
+#define K230_DECOMP_GZIP_GZIP_OUT_SIZE 0x08
+/* Decompress status register */
+#define K230_DECOMP_GZIP_DECOMP_STAT 0x0c
+
+/* Start decompression controller */
+#define K230_DECOMP_GZIP_START (1U << 0)
+
+#define K230_DECOMP_GZIP_CTRL_EN (1U << 31)
+#define K230_DECOMP_GZIP_DMA_IN_MASK 0x7fffffffU
+
+#define K230_DECOMP_GZIP_STAT_CRC_OK (1U << 10)
+#define K230_DECOMP_GZIP_STAT_STATE_MASK 0xfU
+
+#define K230_DECOMP_GZIP_BLOCK_SIZE 0x00020000
+#define K230_DECOMP_GZIP_SRAM_IN_BASE 0x80000
+#define K230_DECOMP_GZIP_SRAM_OUT_BASE 0
+
+typedef struct K230DecompGzipSlotState {
+ uint32_t slot; /* Slot index */
+ /*
+ * Data offset in current slot.
+ * First valid data in input buffer. Last valid data in output buffer.
+ */
+ uint32_t current_offset;
+} K230DecompGzipSlotState;
+
+struct K230DecompGzipState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ hwaddr sram_base;
+ qemu_irq signal_out[K230_DECOMP_GZIP_NUM_GPIOS_OUT];
+ uint32_t decomp_start;
+ uint32_t gzip_src_size;
+ uint32_t gzip_out_size;
+ uint32_t decomp_stat;
+ K230DecompGzipSlotState input; /* decomp gzip ring input */
+ K230DecompGzipSlotState output; /* decomp gzip ring output */
+ uint32_t total_requested;
+ uint32_t total_produced;
+ bool active;
+ bool in_kick;
+ bool zstream_inited;
+ bool stream_end;
+ /* Read from ring input. */
+ uint8_t input_buf[K230_DECOMP_GZIP_BLOCK_SIZE];
+ /* Write to ring output. */
+ uint8_t output_buf[K230_DECOMP_GZIP_BLOCK_SIZE];
+ z_stream zs;
+};
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 5/7] hw/riscv: k230: add decomp gzip in K230 board
2026-07-21 16:13 [PATCH 0/7] K230: add gsdma and decomp_gzip Tao Ding
` (3 preceding siblings ...)
2026-07-21 16:13 ` [PATCH 4/7] hw/misc: add K230 decomp gzip Tao Ding
@ 2026-07-21 16:13 ` Tao Ding
2026-07-21 16:13 ` [PATCH 6/7] tests/qtest: add test for K230 decomp gzip Tao Ding
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Tao Ding @ 2026-07-21 16:13 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-riscv, pbonzini, palmer, alistair.francis, liwei1518,
daniel.barboza, zhiwei_liu, chao.liu, farosas, lvivier, Tao Ding
This commit replaces the unimplemented part of Decomp_gzip in K230.
Connect to the handshake interface of GSDMA.
Signed-off-by: Tao Ding <dingtao0430@163.com>
---
hw/riscv/Kconfig | 1 +
hw/riscv/k230.c | 23 +++++++++++++++++++----
include/hw/riscv/k230.h | 2 ++
3 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index d28ac06043..d06ac26648 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -163,3 +163,4 @@ config K230
select UNIMP
select K230_WDT
select K230_GSDMA
+ select K230_DECOMP_GZIP
diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c
index c80bd65694..7fd1ec4c25 100644
--- a/hw/riscv/k230.c
+++ b/hw/riscv/k230.c
@@ -111,6 +111,8 @@ static void k230_soc_init(Object *obj)
object_initialize_child(obj, "k230-wdt0", &s->wdt[0], TYPE_K230_WDT);
object_initialize_child(obj, "k230-wdt1", &s->wdt[1], TYPE_K230_WDT);
object_initialize_child(obj, "k230-gsdma", &s->gsdma, TYPE_K230_GSDMA);
+ object_initialize_child(obj, "k230-decomp-gzip", &s->decomp_gzip,
+ TYPE_K230_DECOMP_GZIP);
qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0);
qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C908);
@@ -215,6 +217,23 @@ static void k230_soc_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gsdma), 0,
qdev_get_gpio_in(DEVICE(s->c908_plic), K230_GSDMA_IRQ));
+ /* Decomp gzip */
+ qdev_prop_set_uint64(DEVICE(&s->decomp_gzip), "sram-base",
+ memmap[K230_DEV_SRAM].base);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->decomp_gzip), errp)) {
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->decomp_gzip), 0,
+ memmap[K230_DEV_DECOMP_GZIP].base);
+ for (int i = 0; i < K230_DECOMP_GZIP_NUM_GPIOS_OUT; i++) {
+ qdev_connect_gpio_out(DEVICE(&s->decomp_gzip), i,
+ qdev_get_gpio_in(DEVICE(&s->gsdma), i));
+ }
+ for (int i = 0; i < K230_DECOMP_GZIP_NUM_GPIOS_IN; i++) {
+ qdev_connect_gpio_out(DEVICE(&s->gsdma), i,
+ qdev_get_gpio_in(DEVICE(&s->decomp_gzip), i));
+ }
+
/* unimplemented devices */
create_unimplemented_device("kpu.l2-cache",
memmap[K230_DEV_KPU_L2_CACHE].base,
@@ -233,10 +252,6 @@ static void k230_soc_realize(DeviceState *dev, Error **errp)
create_unimplemented_device("dma", memmap[K230_DEV_DMA].base,
memmap[K230_DEV_DMA].size);
- create_unimplemented_device("decomp-gzip",
- memmap[K230_DEV_DECOMP_GZIP].base,
- memmap[K230_DEV_DECOMP_GZIP].size);
-
create_unimplemented_device("2d-engine.non-ai",
memmap[K230_DEV_NON_AI_2D].base,
memmap[K230_DEV_NON_AI_2D].size);
diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h
index 156a386edc..a12d36965a 100644
--- a/include/hw/riscv/k230.h
+++ b/include/hw/riscv/k230.h
@@ -19,6 +19,7 @@
#include "hw/riscv/riscv_hart.h"
#include "hw/watchdog/k230_wdt.h"
#include "hw/dma/k230_gsdma.h"
+#include "hw/misc/k230_decomp_gzip.h"
#define C908_CPU_HARTID (0)
@@ -35,6 +36,7 @@ typedef struct K230SoCState {
K230WdtState wdt[2];
K230GSDMAState gsdma;
+ K230DecompGzipState decomp_gzip;
MemoryRegion sram;
MemoryRegion bootrom;
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 6/7] tests/qtest: add test for K230 decomp gzip
2026-07-21 16:13 [PATCH 0/7] K230: add gsdma and decomp_gzip Tao Ding
` (4 preceding siblings ...)
2026-07-21 16:13 ` [PATCH 5/7] hw/riscv: k230: add decomp gzip in K230 board Tao Ding
@ 2026-07-21 16:13 ` Tao Ding
2026-07-21 16:13 ` [PATCH 7/7] hw/riscv: k230: add a noc stub region in K230 board Tao Ding
2026-07-22 2:03 ` [PATCH 0/7] K230: add gsdma and decomp_gzip Chao Liu
7 siblings, 0 replies; 9+ messages in thread
From: Tao Ding @ 2026-07-21 16:13 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-riscv, pbonzini, palmer, alistair.francis, liwei1518,
daniel.barboza, zhiwei_liu, chao.liu, farosas, lvivier, Tao Ding
This commit adds test cases for Decomp_gzip.
A compressed data segment has been pre-set, configure GSDMA and decomp_gzip.
Compare the decompressed data with the original data
Run this qtest:
$ mkdir build && cd build && ../configure --target-list="riscv64-softmmu"
$ QTEST_QEMU_BINARY=./qemu-system-riscv64 tests/qtest/k230-decomp-gzip-test
Signed-off-by: Tao Ding <dingtao0430@163.com>
---
tests/qtest/k230-decomp-gzip-test.c | 181 ++++++++++++++++++++++++++++
tests/qtest/meson.build | 2 +-
2 files changed, 182 insertions(+), 1 deletion(-)
create mode 100644 tests/qtest/k230-decomp-gzip-test.c
diff --git a/tests/qtest/k230-decomp-gzip-test.c b/tests/qtest/k230-decomp-gzip-test.c
new file mode 100644
index 0000000000..adddf8f2eb
--- /dev/null
+++ b/tests/qtest/k230-decomp-gzip-test.c
@@ -0,0 +1,181 @@
+/*
+ * QTest testcase for K230 GZIP decompression engine
+ *
+ * Copyright (c) 2026 Tao Ding <dingtao0430@163.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "libqtest.h"
+#include "hw/dma/k230_gsdma.h"
+#include "hw/misc/k230_decomp_gzip.h"
+
+#define K230_DECOMP_GZIP_BASE 0x80808000
+#define K230_GSDMA_BASE 0x80800000
+#define K230_SRAM_BASE 0x80200000
+#define TEST_LLT_ADDR0 0x01000000
+#define TEST_LLT_ADDR1 0x01000040
+#define TEST_SRC_ADDR 0x01100000
+#define TEST_DST_ADDR 0x01200000
+
+#define TEST_PAYLOAD_LEN (sizeof(test_payload) - 1)
+
+static inline uint64_t gzip_reg(hwaddr off)
+{
+ return K230_DECOMP_GZIP_BASE + off;
+}
+
+static inline uint64_t gsdma_reg(hwaddr off)
+{
+ return K230_GSDMA_BASE + off;
+}
+
+static inline uint64_t gsdma_ch_reg(unsigned int ch, hwaddr off)
+{
+ return K230_GSDMA_BASE + K230_GSDMA_CH_BASE +
+ ch * K230_GSDMA_CH_STRIDE + off;
+}
+
+static inline hwaddr k230_sram_addr(hwaddr off)
+{
+ return K230_SRAM_BASE + off;
+}
+
+static inline hwaddr k230_sram_input_addr(unsigned int slot)
+{
+ return k230_sram_addr(K230_DECOMP_GZIP_SRAM_IN_BASE +
+ slot * K230_DECOMP_GZIP_BLOCK_SIZE);
+}
+
+static const uint8_t test_gzip_data[] = {
+ 0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0xff, 0xf3, 0x36, 0x32, 0x36, 0x50, 0x48,
+ 0xaf, 0xca, 0x2c, 0x50, 0x28, 0x2e, 0x29, 0x4a,
+ 0x4d, 0xcc, 0xcd, 0xcc, 0x4b, 0x57, 0x28, 0x49,
+ 0x2d, 0x2e, 0xe1, 0xf2, 0x1e, 0x5a, 0x12, 0x00,
+ 0xdb, 0x9d, 0xbc, 0xdd, 0xc8, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t test_payload[] =
+ "K230 gzip streaming test\n"
+ "K230 gzip streaming test\n"
+ "K230 gzip streaming test\n"
+ "K230 gzip streaming test\n"
+ "K230 gzip streaming test\n"
+ "K230 gzip streaming test\n"
+ "K230 gzip streaming test\n"
+ "K230 gzip streaming test\n";
+
+static void write_sdma_llt_node(QTestState *qts, hwaddr addr, uint32_t src,
+ uint32_t dst, uint32_t len, uint32_t next)
+{
+ qtest_writel(qts, addr + offsetof(K230GSDMALLT, cfg), 0);
+ qtest_writel(qts, addr + offsetof(K230GSDMALLT, src_addr), src);
+ qtest_writel(qts, addr + offsetof(K230GSDMALLT, line_size), len);
+ qtest_writel(qts, addr + offsetof(K230GSDMALLT, line_cfg), 0x1);
+ qtest_writel(qts, addr + offsetof(K230GSDMALLT, dst_addr), dst);
+ qtest_writel(qts, addr + offsetof(K230GSDMALLT, next_llt_addr), next);
+}
+
+static void test_reset_and_rw(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_START)),
+ ==, 0);
+ g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_SRC_SIZE)),
+ ==, 0);
+ g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_OUT_SIZE)),
+ ==, 0);
+ g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_STAT)),
+ ==, 0);
+
+ qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_START),
+ K230_DECOMP_GZIP_START);
+
+ qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_SRC_SIZE), 0xffffffff);
+ g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_SRC_SIZE)),
+ ==, 0xffffffff);
+
+ qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_OUT_SIZE), 0x12345678);
+ g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_OUT_SIZE)),
+ ==, 0x12345678);
+
+ qtest_quit(qts);
+}
+
+static void test_gsdma_handshake_flow(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+ uint32_t stat;
+ uint8_t output[TEST_PAYLOAD_LEN];
+
+ qtest_memwrite(qts, TEST_SRC_ADDR, test_gzip_data, sizeof(test_gzip_data));
+ write_sdma_llt_node(qts, TEST_LLT_ADDR0, TEST_SRC_ADDR,
+ k230_sram_input_addr(0),
+ sizeof(test_gzip_data), 0);
+ write_sdma_llt_node(qts, TEST_LLT_ADDR1,
+ k230_sram_addr(K230_DECOMP_GZIP_SRAM_OUT_BASE),
+ TEST_DST_ADDR, TEST_PAYLOAD_LEN, 0);
+
+ qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN), BIT(0) | BIT(1));
+ qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CFG),
+ K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN);
+ qtest_writel(qts, gsdma_ch_reg(1, K230_GSDMA_CH_CFG), 0);
+ qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_LLT_SADDR), TEST_LLT_ADDR0);
+ qtest_writel(qts, gsdma_ch_reg(1, K230_GSDMA_CH_LLT_SADDR), TEST_LLT_ADDR1);
+ qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_START);
+ qtest_writel(qts, gsdma_ch_reg(1, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_START);
+
+ g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN)),
+ ==, BIT(0) | BIT(1));
+ g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CFG)) &
+ K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN,
+ ==, K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN);
+ g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_LLT_SADDR)),
+ ==, TEST_LLT_ADDR0);
+ g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(1, K230_GSDMA_CH_LLT_SADDR)),
+ ==, TEST_LLT_ADDR1);
+
+ qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_SRC_SIZE),
+ K230_DECOMP_GZIP_CTRL_EN | sizeof(test_gzip_data));
+ qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_OUT_SIZE),
+ TEST_PAYLOAD_LEN);
+ qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_START),
+ K230_DECOMP_GZIP_START);
+
+ stat = qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_STAT));
+ g_assert_cmphex(stat & K230_DECOMP_GZIP_STAT_CRC_OK, ==,
+ K230_DECOMP_GZIP_STAT_CRC_OK);
+
+ g_assert_cmphex(qtest_readl(qts,
+ gsdma_ch_reg(0, K230_GSDMA_CH_CURRENT_LLT)),
+ ==, TEST_LLT_ADDR0);
+ g_assert_cmphex(qtest_readl(qts,
+ gsdma_ch_reg(1, K230_GSDMA_CH_CURRENT_LLT)),
+ ==, TEST_LLT_ADDR1);
+
+ stat = qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT));
+ g_assert_cmphex(stat & K230_GSDMA_SDMA_DONE_INT(0), ==,
+ K230_GSDMA_SDMA_DONE_INT(0));
+ g_assert_cmphex(stat & K230_GSDMA_SDMA_DONE_INT(1), ==,
+ K230_GSDMA_SDMA_DONE_INT(1));
+
+ qtest_memread(qts, TEST_DST_ADDR, output, sizeof(output));
+ g_assert_cmpmem(output, sizeof(output), test_payload, TEST_PAYLOAD_LEN);
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/k230-decomp-gzip/reset-and-rw", test_reset_and_rw);
+ qtest_add_func("/k230-decomp-gzip/gsdma-handshake-flow",
+ test_gsdma_handshake_flow);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 1b13a777c9..7da12421af 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -297,7 +297,7 @@ qtests_riscv64 = ['riscv-csr-test'] + \
(config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and
config_all_devices.has_key('CONFIG_RISCV_IOMMU') ?
['iommu-riscv-test'] : []) + \
- (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test', 'k230-gsdma-test'] : [])
+ (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test', 'k230-gsdma-test', 'k230-decomp-gzip-test'] : [])
qtests_hexagon = ['boot-serial-test']
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7/7] hw/riscv: k230: add a noc stub region in K230 board
2026-07-21 16:13 [PATCH 0/7] K230: add gsdma and decomp_gzip Tao Ding
` (5 preceding siblings ...)
2026-07-21 16:13 ` [PATCH 6/7] tests/qtest: add test for K230 decomp gzip Tao Ding
@ 2026-07-21 16:13 ` Tao Ding
2026-07-22 2:03 ` [PATCH 0/7] K230: add gsdma and decomp_gzip Chao Liu
7 siblings, 0 replies; 9+ messages in thread
From: Tao Ding @ 2026-07-21 16:13 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-riscv, pbonzini, palmer, alistair.francis, liwei1518,
daniel.barboza, zhiwei_liu, chao.liu, farosas, lvivier, Tao Ding
The NOC address was accessed in the k230_unzip driver, but it is currently not in k230.
This commit uses create_unimplemented_device to preset the region.
Signed-off-by: Tao Ding <dingtao0430@163.com>
---
Steps to decompress in uboot:
1. Prepare uboot and k230_sdk
u-boot (https://github.com/zevorn/k230-boot-assets)
k230_sdk (https://github.com/kendryte/k230_sdk)
2. Compress image (in sh file, modify ROOT and K230_SDK_ROOT)
ROOT=k230-boot-assets
K230_SDK_ROOT=k230_sdk
DST=$ROOT/buildroot/uboot-boot
cd $DST
$K230_SDK_ROOT/tools/k230_priv_gzip -n8 -f -k Image
sed -i -e "1s/\x08/\x09/" Image.gz
cd -
3. Launch qemu (in sh file, modify ROOT and QEMU)
ROOT=k230-boot-assets
QEMU=qemu/build/qemu-system-riscv64
COMMON=$ROOT/common
DST=$ROOT/buildroot/uboot-boot
INITRD_END=$(printf "0x%x" $((0x0a100000 + $(stat -c %s "$DST/rootfs.cpio.gz"))))
echo "Use this U-Boot initrd end: $INITRD_END"
$QEMU -M k230 \
-bios "$COMMON/u-boot" \
-device loader,file="$COMMON/fw_jump.uImage",addr=0xc100000,force-raw=on \
-device loader,file="$DST/Image.gz",addr=0xd000000,force-raw=on \
-device loader,file="$DST/rootfs.cpio.gz",addr=0xa100000,force-raw=on \
-device loader,file="$DST/k230-qemu.dtb",addr=0xa000000,force-raw=on \
-nographic -no-reboot \
-d guest_errors \
-D qemu.log
4. Input in uboot
k230_unzip 0xd000000 0x6c354f 0x8200000
setenv bootargs console=ttyS0,115200 earlycon=sbi cma=0
fdt addr 0xa000000
fdt resize 8192
fdt set /chosen linux,initrd-start <0x0 0xa100000>
fdt set /chosen linux,initrd-end <0x0 0xba2d5fe>
bootm 0xc100000 - 0xa000000
hw/riscv/k230.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c
index 7fd1ec4c25..ff834d6724 100644
--- a/hw/riscv/k230.c
+++ b/hw/riscv/k230.c
@@ -234,6 +234,9 @@ static void k230_soc_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in(DEVICE(&s->decomp_gzip), i));
}
+ /* Noc stub, the k230 decomp_gzip driver in uboot will access this region */
+ create_unimplemented_device("noc-stub", 0x91302000, 0x1000);
+
/* unimplemented devices */
create_unimplemented_device("kpu.l2-cache",
memmap[K230_DEV_KPU_L2_CACHE].base,
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH 0/7] K230: add gsdma and decomp_gzip
2026-07-21 16:13 [PATCH 0/7] K230: add gsdma and decomp_gzip Tao Ding
` (6 preceding siblings ...)
2026-07-21 16:13 ` [PATCH 7/7] hw/riscv: k230: add a noc stub region in K230 board Tao Ding
@ 2026-07-22 2:03 ` Chao Liu
7 siblings, 0 replies; 9+ messages in thread
From: Chao Liu @ 2026-07-22 2:03 UTC (permalink / raw)
To: Tao Ding
Cc: qemu-devel, qemu-riscv, pbonzini, palmer, alistair.francis,
liwei1518, daniel.barboza, zhiwei_liu, farosas, lvivier,
caojunze424
Hi Tao,
On Wed, Jul 22, 2026 at 12:13:28AM +0800, Tao Ding wrote:
> Hi all,
>
> This patch series adds GSDMA and Decomp_gzip to the K230 board.
> Enable k230 to use k230_unzip to decompress files during the u-boot phase.
>
Thanks for the contribution!
I believe you need to update the K230 documentation. If peripheral modeling
for things like UNGIP and GSDMA has been added, could the boot flow in the
firmware linux booting path be simplified to better align with the hardware's
actual boot process?
cc Junze, Can you help review this? Thanks!
Thanks,
Chao
> This series includes the following features:
>
> *patch1: add K230 gsdma which supports sdma, gdma is temporarily not supported
> *patch2: add gsdma to K230 board
> *patch3: add K230 gsdma qtest
> *patch4: add K230 decomp_gzip which decompress data work with sdma
> *patch5: add decomp_gzip to K230 board
> *patch6: add K230 decomp_gzip qtest
> *patch7: add a noc-stub region in K230 board
>
> Thanks
>
> Tao Ding (7):
> hw/dma: add K230 gsdma
> hw/riscv: k230: add gsdma in K230 board
> tests/qtest: add test for K230 gsdma
> hw/misc: add K230 decomp gzip
> hw/riscv: k230: add decomp gzip in K230 board
> tests/qtest: add test for K230 decomp gzip
> hw/riscv: k230: add a noc stub region in K230 board
>
> hw/dma/Kconfig | 3 +
> hw/dma/k230_gsdma.c | 532 ++++++++++++++++++++++++++++
> hw/dma/meson.build | 1 +
> hw/dma/trace-events | 6 +
> hw/misc/Kconfig | 3 +
> hw/misc/k230_decomp_gzip.c | 498 ++++++++++++++++++++++++++
> hw/misc/meson.build | 1 +
> hw/misc/trace-events | 4 +
> hw/riscv/Kconfig | 2 +
> hw/riscv/k230.c | 38 +-
> include/hw/dma/k230_gsdma.h | 128 +++++++
> include/hw/misc/k230_decomp_gzip.h | 92 +++++
> include/hw/riscv/k230.h | 5 +
> tests/qtest/k230-decomp-gzip-test.c | 181 ++++++++++
> tests/qtest/k230-gsdma-test.c | 201 +++++++++++
> tests/qtest/meson.build | 2 +-
> 16 files changed, 1689 insertions(+), 8 deletions(-)
> create mode 100644 hw/dma/k230_gsdma.c
> create mode 100644 hw/misc/k230_decomp_gzip.c
> create mode 100644 include/hw/dma/k230_gsdma.h
> create mode 100644 include/hw/misc/k230_decomp_gzip.h
> create mode 100644 tests/qtest/k230-decomp-gzip-test.c
> create mode 100644 tests/qtest/k230-gsdma-test.c
>
>
> base-commit: bd6079a7a1d14de0918a7715ef6db85dc32de3bb
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 9+ messages in thread