Re: [PATCH v2 4/7] hw/misc: add K230 decomp gzip

Daniel Henrique Barboza <[email protected]>
Newsgroups org.nongnu.qemu-riscv,org.nongnu.qemu-devel
Message-ID <[email protected]>
Hi,

On 7/27/2026 12:56 PM, Tao Ding wrote:
> 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 <[email protected]>
> ---

LGTM.  Minor comment down below:

> In patchv1, the reviewer identified some bugs, which have been fixed.
> bugs:
>      Decomp gzip only support Dynamic Huffman format
>      Bit 0 in 0x0 register is write only
> enhance:
>      ues new reset interface
> 
>   MAINTAINERS                        |   2 +
>   docs/system/riscv/k230.rst         |   1 +
>   hw/misc/Kconfig                    |   3 +
>   hw/misc/k230_decomp_gzip.c         | 508 +++++++++++++++++++++++++++++
>   hw/misc/meson.build                |   1 +
>   hw/misc/trace-events               |   4 +
>   include/hw/misc/k230_decomp_gzip.h |  92 ++++++
>   7 files changed, 611 insertions(+)
>   create mode 100644 hw/misc/k230_decomp_gzip.c
>   create mode 100644 include/hw/misc/k230_decomp_gzip.h
> 


[...]


> +
> +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;

For all other read/write callbacks I see qemu_log_mask() calls to notice when something
unexpected happens, which is a good practice.

Shouldn't you do the same here?   Seems like you're silently ignoring a foreign interrupt.
Might as well do a qemu_log_mask() here too.


Thanks,
Daniel

> +    }
> +
> +    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 & ~K230_DECOMP_GZIP_START; /* start bit is write only */
> +        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_hold(Object *obj, ResetType type)
> +{
> +    K230DecompGzipState *s = K230_DECOMP_GZIP(obj);
> +
> +    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);
> +    ResettableClass *rc = RESETTABLE_CLASS(oc);
> +
> +    dc->realize = k230_decomp_gzip_realize;
> +    rc->phases.hold = k230_decomp_gzip_reset_hold;
> +    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 <[email protected]>
> + *
> + * 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.