On Thu, 24 Aug 2023 at 19:35, Francisco Iglesias
<francisco.iglesias@amd.com> wrote:
Introduce a model of Xilinx Versal's Configuration Frame controller
(CFRAME_REG).
Signed-off-by: Francisco Iglesias <francisco.iglesias@amd.com>
+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) {
+
+ if (fifo32_num_free(&s->new_f_data) >= N_WORDS_128BIT) {
+ fifo32_push(&s->new_f_data, s->regs[R_FDRI0]);
+ fifo32_push(&s->new_f_data, s->regs[R_FDRI1]);
+ fifo32_push(&s->new_f_data, s->regs[R_FDRI2]);
+ fifo32_push(&s->new_f_data, s->regs[R_FDRI3]);
+ }
+
+ if (fifo32_is_full(&s->new_f_data)) {
+ uint32_t addr = extract32(s->regs[R_FAR0], 0, 23);
+ XlnxCFrame *f = g_new(XlnxCFrame, 1);
+
+ memcpy(f->data, s->new_f_data.fifo.data, sizeof(f->data));
This works, but it's going under the hood of the Fifo32 abstraction
and makes the assumptions that (a) if you only push to the fifo
and never pop then the data is going to be contiguous from the
start of the fifo internal buffer and (b) that fifo32_push()
pushes the bytes of the 32 bit value in little endian order.
Those are both true at the moment, but fifo32 doesn't explicitly
guarantee either of them...
+
+ g_tree_replace(s->cframes, GUINT_TO_POINTER(addr), f);
+
+ cframe_incr_far(s);
+
+ fifo32_reset(&s->new_f_data);
+ }
+ }
+}
+
+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 = g_tree_lookup(s->cframes, GUINT_TO_POINTER(addr));
You don't need to g_tree_lookup() for every address. If
you use g_tree_lookup_node() you get a GTreeNode* back,
and you can then iterate through the tree from that point using
g_tree_node_next(), something like this:
for (node = g_tree_lookup_node(s->cframes, GUINT_TO_POINTER(addr));
node && GPOINTER_TO_UINT(g_tree_node_key(node)) < end_addr;
node = g_tree_node_next(node)) {
XlnxCFrame *f = g_tree_node_value(node); // definitely not NULL
/* Transmit the data */
for (int i = 0; i < FRAME_NUM_WORDS; i += 4) {
... etc ...
}
}
+ register_reset(&s->regs_info[i]);
+ }
+ memset(s->wfifo, 0, WFIFO_SZ * sizeof(uint32_t));
+ fifo32_reset(&s->new_f_data);
+
+ if (g_tree_height(s->cframes)) {
Calculating g_tree_height() requires walking the tree.
Using g_tree_nnodes() is faster because that's just a field
in the GTree struct.
+ g_tree_destroy(s->cframes);
+ s->cframes = g_tree_new_full((GCompareDataFunc)int_cmp, NULL,
+ NULL, (GDestroyNotify) g_free);
Faster to do
/*
* Take a reference so when g_tree_destroy() unrefs it we keep the
* GTree and only destroy its contents. NB: when our minimum
* glib version is at least 2.70 we could use g_tree_remove_all().
*/
g_tree_ref(s->cframes);
g_tree_destroy(s->cframes);
+ }
+}
+
+static void cframe_reg_init(Object *obj)
+{
+ XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ RegisterInfoArray *reg_array;
+
+ memory_region_init(&s->iomem, obj, TYPE_XLNX_VERSAL_CFRAME_REG,
+ CFRAME_REG_R_MAX * 4);
+ reg_array =
+ register_init_block32(DEVICE(obj), cframe_reg_regs_info,
+ ARRAY_SIZE(cframe_reg_regs_info),
+ s->regs_info, s->regs,
+ &cframe_reg_ops,
+ XLNX_VERSAL_CFRAME_REG_ERR_DEBUG,
+ CFRAME_REG_R_MAX * 4);
+ memory_region_add_subregion(&s->iomem,
+ 0x0,
+ ®_array->mem);
+ sysbus_init_mmio(sbd, &s->iomem);
+ memory_region_init_io(&s->iomem_fdri, obj, &cframe_reg_fdri_ops, s,
+ TYPE_XLNX_VERSAL_CFRAME_REG "-fdri",
+ KEYHOLE_STREAM_4K);
+ sysbus_init_mmio(sbd, &s->iomem_fdri);
+ sysbus_init_irq(sbd, &s->irq_cfrm_imr);
+
+ s->cframes = g_tree_new_full((GCompareDataFunc)int_cmp, NULL,
+ NULL, (GDestroyNotify) g_free);
Stray space before 'g_free'.
+ fifo32_create(&s->new_f_data, FRAME_NUM_WORDS);
+}
thanks
-- PMM