On Thu, 10 Aug 2023 at 20:16, Francisco Iglesias
<francisco.igles...@amd.com> wrote:
>
> Introduce a model of Xilinx Versal's Configuration Frame controller
> (CFRAME_REG).
>
> Signed-off-by: Francisco Iglesias <francisco.igles...@amd.com>
> ---
>  MAINTAINERS                              |   2 +
>  hw/misc/meson.build                      |   1 +
>  hw/misc/xlnx-versal-cframe-reg.c         | 753 +++++++++++++++++++++++
>  include/hw/misc/xlnx-versal-cframe-reg.h | 289 +++++++++
>  4 files changed, 1045 insertions(+)
>  create mode 100644 hw/misc/xlnx-versal-cframe-reg.c
>  create mode 100644 include/hw/misc/xlnx-versal-cframe-reg.h

> +static XlnxCFrame *cframes_get_frame(XlnxVersalCFrameReg *s, uint32_t addr)
> +{
> +    for (int i = 0; i < s->cframes->len; i++) {
> +        XlnxCFrame *f = &g_array_index(s->cframes, XlnxCFrame, i);
> +
> +        if (f->addr == addr) {
> +            return f;
> +        }
> +    }
> +    return NULL;
> +}

The handling of this and especially how it turns out in
the migration support still feels quite awkward to me.

The operations we want here seem to be:
 * find a cframe given the 'addr'
 * insert a new cframe for a given 'addr', overwriting any
   old data
 * iterate through n cframes starting at a given 'addr'

You can do this with a GTree
https://developer-old.gnome.org/glib/stable/glib-Balanced-Binary-Trees.html
You can use GUINT_TO_POINTER(addr) as the keys, and use
a Fifo32 as your data. Insert-with-overwrite is
g_tree_replace_node(). Find-a-frame is g_tree_lookup().
Iterate through n cframes is
 for (node = g_tree_lookup(...), i = 0; i < n; node =
g_tree_node_next(node), i++) {
    ...
 }

GTrees are supported by the migration code, there is a
VMSTATE_GTREE_DIRECT_KEY_V() macro, so you don't need to
do any pre-save or post-load hooks. (This to me is one
of the main benefits of using it rather than a GArray.)

Is the data in each cframe fixed-size, or can it vary?
The impression I get is that each cframe is always the
same amount of data, and we use a fifo purely to handle
the "guest writes the frame data a word at a time and
when it's all arrived we put it into the cframe data
structure". If so, it might be simpler to use a fifo32
for the new_f, but have the data in the gtree structure
be a simple fixed-size block of memory.

> +
> +static void cframe_alloc(XlnxCFrame *f)
> +{
> +    f->addr = 0;
> +    fifo32_create(&f->data, FRAME_NUM_WORDS);
> +}
> +
> +static void cframe_move(XlnxCFrame *dst, XlnxCFrame *src)
> +{
> +    fifo32_destroy(&dst->data);
> +    dst[0] = src[0];
> +}
> +
> +static void cfrm_fdri_post_write(RegisterInfo *reg, uint64_t val)
> +{
> +    XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque);
> +
> +    if (s->row_configured && s->rowon && s->wcfg) {
> +        XlnxCFrame *new_f = &s->new_f;
> +
> +        if (fifo32_num_free(&new_f->data) >= N_WORDS_128BIT) {
> +            fifo32_push(&new_f->data, s->regs[R_FDRI0]);
> +            fifo32_push(&new_f->data, s->regs[R_FDRI1]);
> +            fifo32_push(&new_f->data, s->regs[R_FDRI2]);
> +            fifo32_push(&new_f->data, s->regs[R_FDRI3]);
> +        }
> +
> +        if (fifo32_is_full(&new_f->data)) {
> +            XlnxCFrame *cur_f;
> +
> +            /* Include block type and frame address */
> +            new_f->addr = extract32(s->regs[R_FAR0], 0, 23);
> +
> +            cur_f = cframes_get_frame(s, new_f->addr);
> +
> +            if (cur_f) {
> +                cframe_move(cur_f, new_f);
> +            } else {
> +                g_array_append_val(s->cframes, new_f[0]);
> +            }
> +
> +            cframe_incr_far(s);
> +
> +            /* Realloc new_f */
> +            cframe_alloc(new_f);
> +        }
> +    }
> +}
> +
> +static void cfrm_readout_frames(XlnxVersalCFrameReg *s, uint32_t start_addr,
> +                                uint32_t end_addr)
> +{
> +    for (uint32_t addr = start_addr; addr < end_addr; addr++) {
> +        XlnxCFrame *f = cframes_get_frame(s, addr);
> +
> +        /* Transmit the data if a frame was found */
> +        if (f) {
> +            Fifo32 data = f->data;
> +
> +            while (!fifo32_is_empty(&data)) {
> +                XlnxCfiPacket pkt = {};
> +
> +                g_assert(fifo32_num_used(&data) >= N_WORDS_128BIT);
> +
> +                pkt.data[0] = fifo32_pop(&data);
> +                pkt.data[1] = fifo32_pop(&data);
> +                pkt.data[2] = fifo32_pop(&data);
> +                pkt.data[3] = fifo32_pop(&data);
> +
> +                if (s->cfg.cfu_fdro) {
> +                    xlnx_cfi_transfer_packet(s->cfg.cfu_fdro, &pkt);
> +                }
> +            }
> +        }
> +    }
> +}
> +
> +static void cfrm_frcnt_post_write(RegisterInfo *reg, uint64_t val)
> +{
> +    XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque);
> +
> +    if (s->row_configured && s->rowon && s->rcfg) {
> +        uint32_t start_addr = extract32(s->regs[R_FAR0], 0, 23);
> +        uint32_t end_addr = start_addr + s->regs[R_FRCNT0] / 
> FRAME_NUM_QWORDS;
> +
> +        cfrm_readout_frames(s, start_addr, end_addr);
> +    }
> +}

> +static void cframe_reg_cfi_transfer_packet(XlnxCfiIf *cfi_if,
> +                                           XlnxCfiPacket *pkt)
> +{
> +    XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(cfi_if);
> +    uint64_t we = MAKE_64BIT_MASK(0, 4 * 8);
> +
> +    if (!s->row_configured) {
> +        return;
> +    }
> +
> +    switch (pkt->reg_addr) {
> +    case CFRAME_FAR:
> +        s->regs[R_FAR0] = pkt->data[0];
> +        break;
> +    case CFRAME_SFR:
> +        s->regs[R_FAR_SFR0] = pkt->data[0];
> +        register_write(&s->regs_info[R_FAR_SFR3], 0,
> +                       we, object_get_typename(OBJECT(s)),
> +                       XLNX_VERSAL_CFRAME_REG_ERR_DEBUG);
> +        break;
> +    case CFRAME_FDRI:
> +    {
> +        s->regs[R_FDRI0] = pkt->data[0];
> +        s->regs[R_FDRI1] = pkt->data[1];
> +        s->regs[R_FDRI2] = pkt->data[2];
> +        register_write(&s->regs_info[R_FDRI3], pkt->data[3],
> +                       we, object_get_typename(OBJECT(s)),
> +                       XLNX_VERSAL_CFRAME_REG_ERR_DEBUG);
> +        break;
> +    }

The braces here seem to be unnecessary ?

> +    case CFRAME_CMD:
> +        ARRAY_FIELD_DP32(s->regs, CMD0, CMD, pkt->data[0]);
> +
> +        register_write(&s->regs_info[R_CMD3], 0,
> +                       we, object_get_typename(OBJECT(s)),
> +                       XLNX_VERSAL_CFRAME_REG_ERR_DEBUG);
> +        break;
> +    default:
> +        break;
> +    }
> +}

> +static void cframe_reg_reset_enter(Object *obj, ResetType type)
> +{
> +    XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj);
> +    unsigned int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
> +        register_reset(&s->regs_info[i]);
> +    }
> +    memset(s->wfifo, 0, WFIFO_SZ * sizeof(uint32_t));

Doesn't reset also need to do something about s->new_f
and the other cframes ?

> +}

> +static int cframes_reg_pre_save(void *opaque)
> +{
> +    XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(opaque);
> +    uint32_t *cf_data;
> +
> +    s->cf_dlen = s->cframes->len * MIG_CFRAME_SZ;
> +    s->cf_data = g_new(uint8_t, s->cf_dlen);
> +
> +    cf_data = (uint32_t *) s->cf_data;
> +
> +    for (int i = 0; i < s->cframes->len; i++) {
> +        XlnxCFrame *f = &g_array_index(s->cframes, XlnxCFrame, i);
> +        Fifo32 data = f->data;
> +
> +        *cf_data++ = f->addr;
> +
> +        while (!fifo32_is_empty(&data)) {
> +            *cf_data++ = fifo32_pop(&data);
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int cframes_reg_post_load(void *opaque, int version_id)
> +{
> +    XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(opaque);
> +
> +    if (s->cf_dlen) {
> +        uint32_t num_frames = s->cf_dlen / MIG_CFRAME_SZ;
> +        uint32_t *cf_data = (uint32_t *) s->cf_data;
> +        XlnxCFrame new_f;
> +
> +        for (int i = 0; i < num_frames; i++) {
> +            cframe_alloc(&new_f);
> +
> +            new_f.addr = *cf_data++;
> +
> +            while (!fifo32_is_full(&new_f.data)) {
> +                fifo32_push(&new_f.data, *cf_data++);
> +            }
> +
> +            g_array_append_val(s->cframes, new_f);
> +        }
> +    }
> +
> +    g_free(s->cf_data);
> +    s->cf_data = NULL;
> +    s->cf_dlen = 0;
> +
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_cframe_reg = {
> +    .name = TYPE_XLNX_VERSAL_CFRAME_REG,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .pre_save = cframes_reg_pre_save,
> +    .post_load = cframes_reg_post_load,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFrameReg, 4),
> +        VMSTATE_UINT32_ARRAY(regs, XlnxVersalCFrameReg, CFRAME_REG_R_MAX),
> +        VMSTATE_BOOL(rowon, XlnxVersalCFrameReg),
> +        VMSTATE_BOOL(wcfg, XlnxVersalCFrameReg),
> +        VMSTATE_BOOL(rcfg, XlnxVersalCFrameReg),
> +        VMSTATE_VARRAY_UINT32_ALLOC(cf_data, XlnxVersalCFrameReg, cf_dlen,
> +                                    0, vmstate_info_uint8, uint8_t),
> +        VMSTATE_END_OF_LIST(),

This seems to omit migration of s->new_f.

> +    }
> +};
> +
> +static Property cframe_regs_props[] = {
> +    DEFINE_PROP_LINK("cfu-fdro", XlnxVersalCFrameReg, cfg.cfu_fdro,
> +                     TYPE_XLNX_CFI_IF, XlnxCfiIf *),
> +    DEFINE_PROP_UINT32("blktype0-frames", XlnxVersalCFrameReg,
> +                       cfg.blktype_num_frames[0], 0),
> +    DEFINE_PROP_UINT32("blktype1-frames", XlnxVersalCFrameReg,
> +                       cfg.blktype_num_frames[1], 0),
> +    DEFINE_PROP_UINT32("blktype2-frames", XlnxVersalCFrameReg,
> +                       cfg.blktype_num_frames[2], 0),
> +    DEFINE_PROP_UINT32("blktype3-frames", XlnxVersalCFrameReg,
> +                       cfg.blktype_num_frames[3], 0),
> +    DEFINE_PROP_UINT32("blktype4-frames", XlnxVersalCFrameReg,
> +                       cfg.blktype_num_frames[4], 0),
> +    DEFINE_PROP_UINT32("blktype5-frames", XlnxVersalCFrameReg,
> +                       cfg.blktype_num_frames[5], 0),
> +    DEFINE_PROP_UINT32("blktype6-frames", XlnxVersalCFrameReg,
> +                       cfg.blktype_num_frames[6], 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void cframe_reg_class_init(ObjectClass *klass, void *data)
> +{
> +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    XlnxCfiIfClass *xcic = XLNX_CFI_IF_CLASS(klass);
> +
> +    dc->vmsd = &vmstate_cframe_reg;
> +    dc->realize = cframe_reg_realize;
> +    rc->phases.enter = cframe_reg_reset_enter;
> +    rc->phases.hold = cframe_reg_reset_hold;
> +    device_class_set_props(dc, cframe_regs_props);
> +    xcic->cfi_transfer_packet = cframe_reg_cfi_transfer_packet;
> +}
> +
> +static const TypeInfo cframe_reg_info = {
> +    .name          = TYPE_XLNX_VERSAL_CFRAME_REG,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(XlnxVersalCFrameReg),
> +    .class_init    = cframe_reg_class_init,
> +    .instance_init = cframe_reg_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_XLNX_CFI_IF },
> +        { }
> +    }
> +};
> +
> +static void cframe_reg_register_types(void)
> +{
> +    type_register_static(&cframe_reg_info);
> +}
> +
> +type_init(cframe_reg_register_types)

thanks
-- PMM

Reply via email to