LIU Zhiwei <zhiwei_...@c-sky.com> 於 2021年6月29日 週二 上午10:45寫道:

> Hi Frank,
>
> Thanks for a lot of good points.
> On 2021/6/26 下午11:03, Frank Chang wrote:
>
> LIU Zhiwei <zhiwei_...@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:
>
>> +static uint8_t
>> +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
>> +{
>> +    int nlbits = clic->nlbits;
>> +
>> +    uint8_t mask_il = ((1 << nlbits) - 1) << (8 - nlbits);
>> +    uint8_t mask_padding = (1 << (8 - nlbits)) - 1;
>> +    /* unused level bits are set to 1 */
>> +    return (intctl & mask_il) | mask_padding;
>> +}
>>
>
> According to spec:
>   if the nlbits > CLICINTCTLBITS, then the lower bits of the 8-bit
>   interrupt level are assumed to be all 1s.
>
> That is, the valid nlbits should be: min(clic->nlbits, CLICINTCTLBITS);
> The cliccfg example in spec also shows that:
>
> CLICINTCTLBITS  nlbits  clicintctl[i]  interrupt levels
>       0                       2         ........         255
>       1                       2         l.......         127,255
>       2                       2         ll......         63,127,191,255
>       3                       3         lll.....
>  31,63,95,127,159,191,223,255
>       4                       1         lppp....      127,255
>
>
> Agree, thanks.
>
>
>> + * In a system with multiple harts, the M-mode CLIC regions for all the
>> harts
>> + * are placed contiguously in the memory space, followed by the S-mode
>> CLIC
>> + * regions for all harts. (Section 3.11)
>> + */
>>
>
> The above description is not true any more in the latest spec:
>   The CLIC specification does not dictate how CLIC memory-mapped registers
> are
>   split between M/S/U regions as well as the layout of multiple harts as
> this is generally
>   a platform issue and each platform needs to define a discovery mechanism
> to determine
>   the memory map locations.
>
> But I think we can just keep the current design for now anyway, as it's
> also one of legal memory layout.
> Otherwise, the design would be more complicated I think.
>
> We can pass an array containing indexed by hart_id and mode from the
> machine board, such as
>
> hwaddr clic_memmap[HARTS][MODE] = {
>
> {0x0000, 0x10000, 0x20000},
>
> {0x4000, 0x14000, 0x24000},
>
> {0x8000, 0x18000, 0x28000},
>
> {0xc000, 0x1c000, 0x2c000},
>
> }
>
That would be great.
We can create different memory regions for each memory map
and assign them with the same read/write ops.

Thanks,
Frank Chang


>
>
>>
>> +static void
>> +riscv_clic_update_intie(RISCVCLICState *clic, int mode, int hartid,
>> +                        int irq, uint64_t new_intie)
>> +{
>> +    size_t hart_offset = hartid * clic->num_sources;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
>> +    size_t *active_count = &clic->active_count[hartid];
>> +
>> +    uint8_t old_intie = clic->clicintie[irq_offset];
>> +    clic->clicintie[irq_offset] = !!new_intie;
>> +
>> +    /* Add to or remove from list of active interrupts */
>> +    if (new_intie && !old_intie) {
>> +        active_list[*active_count].intcfg = (mode << 8) |
>> +                                            clic->clicintctl[irq_offset];
>> +        active_list[*active_count].irq = irq;
>> +        (*active_count)++;
>> +    } else if (!new_intie && old_intie) {
>> +        CLICActiveInterrupt key = {
>> +            (mode << 8) | clic->clicintctl[irq_offset], irq
>> +        };
>> +        CLICActiveInterrupt *result = bsearch(&key,
>> +                                              active_list, *active_count,
>> +
>> sizeof(CLICActiveInterrupt),
>> +                                              riscv_clic_active_compare);
>> +        size_t elem = (result - active_list) /
>> sizeof(CLICActiveInterrupt);
>>
>
> I think what you are trying to do here is to get the number of elements
> right after the active interrupt to be deleted in order to calculate the
> size of
> active interrupts to be memmoved.
>
> Agree, thanks.
>
> However, according to C spec:
>   When two pointers are subtracted, both shall point to elements of the
> same array object,
>   or one past the last element of the array object; the result is the
> difference of the
>   subscripts of the two array elements.
>
> So I think: (result - active_list) is already the number of elements you
> want.
> You don't have to divide it with sizeof(CLICActiveInterrupt) again.
>
>
>> +        size_t sz = (--(*active_count) - elem) *
>> sizeof(CLICActiveInterrupt);
>> +        assert(result);
>>
>
> Nit: assert(result) can be moved above size_t elem statement.
>
> Agree.
>
>
>
>> +        memmove(&result[0], &result[1], sz);
>> +    }
>> +
>> +    /* Sort list of active interrupts */
>> +    qsort(active_list, *active_count,
>> +          sizeof(CLICActiveInterrupt),
>> +          riscv_clic_active_compare);
>> +
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +static void
>> +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
>> +                      uint64_t value, unsigned size,
>> +                      int mode, int hartid, int irq)
>> +{
>> +    int req = extract32(addr, 0, 2);
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +
>> +    if (hartid >= clic->num_harts) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
>> +                      hartid, addr);
>> +        return;
>> +    }
>> +
>> +    if (irq >= clic->num_sources) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid irq %u: 0x%" HWADDR_PRIx "\n", irq,
>> addr);
>> +        return;
>> +    }
>> +
>> +    switch (req) {
>>
>
> Spec. says that it's legal to write 32-bit value to set
> {clicintctl[i], clicintattr[i], clicintie[i] ,clicintip[i]} at the same
> time:
>   A 32-bit write to {clicintctl,clicintattr,clicintie,clicintip} is legal.
>   However, there is no specified order in which the effects of
>   the individual byte updates take effect.
>
>
> I miss it. I think it only supports 1 byte access or 4 bytes access. For 4
> bytes access,  we need to pass an flag to specify to the order from the
> machine board.
>
> Thoughts?
>
> +    case 0: /* clicintip[i] */
>> +        if (riscv_clic_validate_intip(clic, mode, hartid, irq)) {
>> +            /*
>> +             * The actual pending bit is located at bit 0 (i.e., the
>> +             * leastsignificant bit). In case future extensions expand
>> the bit
>>
>
> Typo: leastsignificant => least significant
>
> OK.
>
>
>
>> +             * field, from FW perspective clicintip[i]=zero means no
>> interrupt
>> +             * pending, and clicintip[i]!=0 (not just 1) indicates an
>> +             * interrupt is pending. (Section 3.4)
>> +             */
>> +            if (value != clic->clicintip[irq_offset]) {
>> +                riscv_clic_update_intip(clic, mode, hartid, irq, value);
>> +            }
>> +        }
>> +        break;
>> +    case 1: /* clicintie[i] */
>> +        if (clic->clicintie[irq_offset] != value) {
>> +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
>> +        }
>> +        break;
>> +    case 2: /* clicintattr[i] */
>> +        if (riscv_clic_validate_intattr(clic, value)) {
>> +            if (clic->clicintattr[irq_offset] != value) {
>> +                /* When nmbits=2, check WARL */
>> +                bool invalid = (clic->nmbits == 2) &&
>> +                               (extract64(value, 6, 2) == 0b10);
>> +                if (invalid) {
>> +                    uint8_t old_mode =
>> extract32(clic->clicintattr[irq_offset],
>> +                                                 6, 2);
>> +                    value = deposit32(value, 6, 2, old_mode);
>> +                }
>> +                clic->clicintattr[irq_offset] = value;
>> +                riscv_clic_next_interrupt(clic, hartid);
>> +            }
>> +        }
>> +        break;
>> +    case 3: /* clicintctl[i] */
>> +        if (value != clic->clicintctl[irq_offset]) {
>> +            clic->clicintctl[irq_offset] = value;
>>
>
> If irq i is already in the active_list, when will its intcfg been synced?
>
> Good. I think should sync immediately.
>
>
>
>> +            riscv_clic_next_interrupt(clic, hartid);
>> +        }
>> +        break;
>> +    }
>> +}
>> +
>> +static uint64_t
>> +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
>> +                     int hartid, int irq)
>> +{
>> +    int req = extract32(addr, 0, 2);
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +
>> +    if (hartid >= clic->num_harts) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
>> +                      hartid, addr);
>> +        return 0;
>> +    }
>> +
>> +    if (irq >= clic->num_sources) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid irq %u: 0x%" HWADDR_PRIx "\n", irq,
>> addr);
>> +        return 0;
>> +    }
>> +
>> +    switch (req) {
>> +    case 0: /* clicintip[i] */
>> +        return clic->clicintip[irq_offset];
>> +    case 1: /* clicintie[i] */
>> +        return clic->clicintie[irq_offset];
>> +    case 2: /* clicintattr[i] */
>> +        /*
>> +         * clicintattr register layout
>> +         * Bits Field
>> +         * 7:6 mode
>> +         * 5:3 reserved (WPRI 0)
>> +         * 2:1 trig
>> +         * 0 shv
>> +         */
>> +        return clic->clicintattr[irq_offset] & ~0x38;
>> +    case 3: /* clicintctrl */
>>
>
> Typo: clicintctl
>
> OK.
>
>
>
>> +        /*
>> +         * The implemented bits are kept left-justified in the
>> most-significant
>> +         * bits of each 8-bit clicintctl[i] register, with the lower
>> +         * unimplemented bits treated as hardwired to 1.(Section 3.7)
>> +         */
>> +        return clic->clicintctl[irq_offset] |
>> +               ((1 << (8 - clic->clicintctlbits)) - 1);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>>
>> +static void
>> +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned
>> size)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    hwaddr clic_size = clic->clic_size;
>> +    int hartid, mode, irq;
>> +
>> +    if (addr < clic_size) {
>>
>
> Is this necessary?
> I think memory region size already limits the request address to be within
> the range of clic_size.
>
> At this point, not necessary.
>
>
>
>>
>> +static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    hwaddr clic_size = clic->clic_size;
>> +    int hartid, mode, irq;
>> +
>> +    if (addr < clic_size) {
>>
>
> Same to riscv_clic_write().
>
> Thanks,
> Frank Chang
>
>
>> +        if (addr < 0x1000) {
>> +            assert(addr % 4 == 0);
>> +            int index = addr / 4;
>> +            switch (index) {
>> +            case 0: /* cliccfg */
>> +                return clic->nvbits |
>> +                       (clic->nlbits << 1) |
>> +                       (clic->nmbits << 5);
>> +            case 1: /* clicinfo */
>> +                /*
>> +                 * clicinfo register layout
>> +                 *
>> +                 * Bits Field
>> +                 * 31 reserved (WARL 0)
>> +                 * 30:25 num_trigger
>> +                 * 24:21 CLICINTCTLBITS
>> +                 * 20:13 version (for version control)
>> +                 * 12:0 num_interrupt
>> +                 */
>> +                return clic->clicinfo & ~INT32_MAX;
>>
>
> clic->clicinfo should represent the CLIC setting information.
> I think it's better to add clic reset function or in riscv_clic_realize()
> to initialize clic->clicinfo.
>
> Agree.
>
>
>
>> +
>> +static void riscv_clic_realize(DeviceState *dev, Error **errp)
>> +{
>> +    RISCVCLICState *clic = RISCV_CLIC(dev);
>> +    size_t harts_x_sources = clic->num_harts * clic->num_sources;
>> +    int irqs, i;
>> +
>> +    if (clic->prv_s && clic->prv_u) {
>> +        irqs = 3 * harts_x_sources;
>> +    } else if (clic->prv_s || clic->prv_u) {
>> +        irqs = 2 * harts_x_sources;
>> +    } else {
>> +        irqs = harts_x_sources;
>> +    }
>> +
>> +    clic->clic_size = irqs * 4 + 0x1000;
>> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops,
>> clic,
>> +                          TYPE_RISCV_CLIC, clic->clic_size);
>> +
>> +    clic->clicintip = g_new0(uint8_t, irqs);
>> +    clic->clicintie = g_new0(uint8_t, irqs);
>> +    clic->clicintattr = g_new0(uint8_t, irqs);
>> +    clic->clicintctl = g_new0(uint8_t, irqs);
>> +    clic->active_list = g_new0(CLICActiveInterrupt, irqs);
>>
>
> Should the size of clic->active_list be: harts_x_sources?
>
> Every irq can be in the active_list, so just the number of irqs.
>
> Remind we will only use M-mode IRQ in next patch set, I still think we
> should use the
> irqs here, because irq in active_list has privilege information.
>
> Thanks,
> Zhiwei
>
>
>
>>
>>

Reply via email to