Re: [PATCH v2 4/7] hw/misc: add K230 decomp gzip
Daniel Henrique Barboza <[email protected]>
| Newsgroups | gmane.comp.emulators.qemu |
|---|---|
| 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