chipsalliance/Cores-VeeR-EH1

Erasing pending bits for interrupts

albertodbg opened this issue · 25 comments

Hi, after some changes my code is working on SwerVolf 0.7 and external interrupts are configured, the handler is correctly identified. However, I am unable to clear the pending bits of the external interrupts. After updating the leds with the content of the switches, I have tried this code (and several variations)

_li t0, (RV_PIC_BASE_ADDR + RV_PIC_MEIGWCLR_OFFSET + 0x14)
sw x1, 0(t0)
lw t1, 0(t0)

csrr tp, mip
andi t0, tp, 0x7FF
csrw mip, t0_

However, the pending bit is still active and after coming back to the main loop, the handler is executed again and again. After running step by step, I have noticed that the mip register is never modified. I have read in the documentation that only the supervisor bits can be written, but I am working in M mode, so I guess that I have to erase the 11th bit (MEIP). Using the meigwclr register alone or in combination with the mip erasing, has no effect either. How should this be done? Am I missing something?

MIP (Interrupt Pending register) reflects pending interrupt requests. MIP writing does not change its state. The requests need to be cleared in the source. For PIC generated interrupts it need to be done in its sources. It was not clear what are the interrupt sources in your case. For ex, if an interrupt request is caused by a UART receiving a data byte, usually the data need to be read from UART data register to de-assert it's interrupt request, if the corresponded PIC input is programmed as level sensitive. meigwclrN register need to be used to clear pending request if the PIC input is configured as edge sensitive ...

The interrupts are configured to be edge triggered. Let me attach the code (sorry, it is a little dirty as I am testing things). Also, there are many load-stores because the memory viewer is not working well in Code.

LedsSwitches.S.txt

Thus, I am trying to erase the pending bit using the meigwclr. But for some reason it is not doing anything. The address should be fine, as it is the only enabled interrupt (and I can see that when reading from memory). And in fact the interrupt is enabled, so gateway 5 should be the one.

Ok, I have been cleaning the code a little bit. I was reading the meip registers to see if the interrupt flag was cleared, but it was not.

LedsSwitches_clean.S.txt

I am serving the external interrupt (gpio_irq is named in the verilog code). The idea is to use the switches, but even without touching them, the interrupt is pending and the handler is triggered.

can you single step through your handler and show what the GPR values for each instruction executed in there?

Albertodbg, your basic flow looks OK, and should work. Here is an example waveform and instruction trace for a negative edge triggered interrupt[5] sequence:
EH1-65

can you single step through your handler and show what the GPR values for each instruction executed in there?

Here it is. I have modified the destination register: t1 instead of x1. I have realized that x1 is the ra register, but the same behavior remains. Here is the content of the general purpose registers prior to clearing

Captura de pantalla 2020-07-10 a las 16 42 45

And here it is after clearing the pending interrupt

Captura de pantalla 2020-07-10 a las 16 43 11

Also, it can be observed that there is a pending interrupt in the t1 register, while the memory viewer is totally blank when showing the meip space.

Are you sure that the input 5 does not toggle?
Regarding PIC memory dump - check that your debugger supports memory access from Swerv internal memories ...

Are you sure that the input 5 does not toggle?
If I cannot see the memory content, how I am supposed to see this? Loading the program and running a simulation through gtkwave or similar? I mean, I am checking the meip content by loading to a register, but line 5 is always active. Even at the beginning of the program, without interacting with the switches.
Regarding PIC memory dump - check that your debugger supports memory access from Swerv internal memories ...
I am using riscv-openocd. Is there a special tweak to allow access? These are the options that are configured in my .cfg file
riscv set_prefer_sba on
riscv set_ir idcode 0x9
riscv set_ir dmi 0x22
riscv set_ir dtmcs 0x23

Can you use a scope?

I think, openocd does not support read from internal memories ( including PIC) - these are accessed with memory abstract command only.

Seems SwerVolf ties off irq5 (?)
Hey, Alberto, seems what you see is what was designed ... See gateway diagram in the PRM (Fig 6-3). if the irq input is 0, polarity is 1 and edged mode - the pending req is set immediately after writing meigwclr ...

I think, openocd does not support read from internal memories ( including PIC) - these are accessed with memory abstract command only.

That is correct, the current version of riscv-openocd does not allow to access internal memories (ICCM, DCCM and PIC). There is a patched OpenOCD version for SweRV that has proper support for accessing the internal memories: https://github.com/Codasip/swerv-openocd. If you'd like to try it, please see the README for instructions.

Hi @albertodbg,
Please note that the gateway does not implement any edge-detection logic (e.g., an edge-triggered flop) to convert the interrupt request to a level-triggered interrupt signal (see Figure 6-3 of the PRM). Therefore, the interrupt request input signal must be set to the inactive level (i.e., to ‘0’ for an active-high interrupt and to ‘1’ for an active-low interrupt) to avoid an interrupt request being continuously reported as pending, even after the gateway’s IP latch has been cleared. Consequently, if the gateway of an unused interrupt request input is programmed to an “active-high” polarity, the interrupt input signal must be tied off to ‘0’. Likewise, if the polarity is programmed to “active-low”, the interrupt input signal must be tied off to ‘1’.
We will add a note to the PRM to clarify this.

Hi @albertodbg,
Please note that the gateway does not implement any edge-detection logic (e.g., an edge-triggered flop) to convert the interrupt request to a level-triggered interrupt signal (see Figure 6-3 of the PRM). Therefore, the interrupt request input signal must be set to the inactive level (i.e., to ‘0’ for an active-high interrupt and to ‘1’ for an active-low interrupt) to avoid an interrupt request being continuously reported as pending, even after the gateway’s IP latch has been cleared. Consequently, if the gateway of an unused interrupt request input is programmed to an “active-high” polarity, the interrupt input signal must be tied off to ‘0’. Likewise, if the polarity is programmed to “active-low”, the interrupt input signal must be tied off to ‘1’.
We will add a note to the PRM to clarify this.

Ok. But how am I supposed to do this with the PIC registers? Using meigwclr is not working and the meip registers only have reading permission, so I cannot erase the pending bit. Then, how can I deassert extintsrc_req? (in this case bit 5).

On other hand, Table 6-11 and this code in the PRM seem to be incoherent, since type is mapped on bit 1 and polarity on bit 0. In any case, I had both set to 1, so this is not part of the problem.
.macro init_gateway id, polarity, type
init_gateway_@:
li tp, (RV_PIC_BASE_ADDR + RV_PIC_MEIGWCTRL_OFFSET + (\id <<2))
li t0, ((\polarity<<1) | \type)
sw t0, 0(tp)
.endm

You can 1) not to enable tied off interrupts; 2) not to program polarity to 1 for input 5;

You can 1) not to enable tied off interrupts; 2) not to program polarity to 1 for input 5;

According to Table 6-11

type | 1 | External interrupt type for interrupt source ID S: 0: Level-triggered interrupt 1: Edge-triggered interrupt |   |  
polarity | 0 | External interrupt polarity for interrupt source ID S: 0: Active-high interrupt 1: Active-low interrupt |   |  

I have tested several configurations and what matters is polarity, the type value (level vs edge) does not have any effect. I mean, if polarity is 1, I am going through the handler forever and ever (even without moving the switches); and if polarity is 0, the handler is never triggered. So any of the configurations is working well

I don't think you can use tied off interrupts inputs, like #5 in SwerVolf. You will get or no or permanent request. The interrupt request to work some agent needs to assert it and de-assert it when this request is served by the CPU.
You can model such behavior with a GPIO - if you connect the GPIO output to an interrupt request. Setting the GPIO in main will cause interrupt request, then in handler you need to reset the GPIO before exiting the handler.

Or you can use SwerVolf peripherals to generate interrupt requests on connected PIC inputs. Consult with Olof how to accomplish this. (I think UART is connected to PIC, you can program the UART to generate interrupt when it transmits a byte).

Hi @albertodbg,

On other hand, Table 6-11 and this code in the PRM seem to be incoherent, since type is mapped on bit 1 and polarity on bit 0. In any case, I had both set to 1, so this is not part of the problem.
.macro init_gateway id, polarity, type
init_gateway_@:
li tp, (RV_PIC_BASE_ADDR + RV_PIC_MEIGWCTRL_OFFSET + (\id <<2))
li t0, ((\polarity<<1) | \type)
sw t0, 0(tp)
.endm

You are right. The example macro to initialize the gateway is incorrect. This will be fixed in the next release of the PRM.

Thank you for bringing this to our attention!

I think, openocd does not support read from internal memories ( including PIC) - these are accessed with memory abstract command only.

That is correct, the current version of riscv-openocd does not allow to access internal memories (ICCM, DCCM and PIC). There is a patched OpenOCD version for SweRV that has proper support for accessing the internal memories: https://github.com/Codasip/swerv-openocd. If you'd like to try it, please see the README for instructions.

I am trying to debut using this version of openocd. However I am getting the following error:

Warn : Note: Command add_abstract_mem_range is a custom workaround for SweRV.
Warn : (Not part of the official upstream OpenOCD.)
Info : tcl server disabled
Info : telnet server disabled
Warn : Note: This is custom OpenOCD version with extensions and workarounds for SweRV cores.
Info : clock speed 5000 kHz
Info : JTAG tap: riscv.cpu tap/device found: 0x13631093 (mfg: 0x049 (Xilinx), part: 0x3631, ver: 0x1)
Error: Unsupported DTM version: 9
Info : starting gdb server for riscv.cpu on pipe
Info : accepting 'gdb' connection from pipe
Error: Target not examined yet
Error executing event gdb-attach on target riscv.cpu:

Error: Target not initialized. Return ERROR_FAIL.
Error: Target not initialized. Return ERROR_FAIL.
Error: get register list failed
Info : Failed detecting Target Description Support, disabling
warning: Remote failure reply: E01
Error: Target not initialized. Return ERROR_FAIL.
.pioinit:13: Error in sourced command file:
Could not read registers; remote failure reply 'E0E'

I attach the whole trace when debugging with -d3
swerv-openocd_full_trace.txt

I think, openocd does not support read from internal memories ( including PIC) - these are accessed with memory abstract command only.

That is correct, the current version of riscv-openocd does not allow to access internal memories (ICCM, DCCM and PIC). There is a patched OpenOCD version for SweRV that has proper support for accessing the internal memories: https://github.com/Codasip/swerv-openocd. If you'd like to try it, please see the README for instructions.

I am trying to debut using this version of openocd. However I am getting the following error:

Warn : Note: Command add_abstract_mem_range is a custom workaround for SweRV.
Warn : (Not part of the official upstream OpenOCD.)
Info : tcl server disabled
Info : telnet server disabled
Warn : Note: This is custom OpenOCD version with extensions and workarounds for SweRV cores.
Info : clock speed 5000 kHz
Info : JTAG tap: riscv.cpu tap/device found: 0x13631093 (mfg: 0x049 (Xilinx), part: 0x3631, ver: 0x1)
Error: Unsupported DTM version: 9
Info : starting gdb server for riscv.cpu on pipe
Info : accepting 'gdb' connection from pipe
Error: Target not examined yet
Error executing event gdb-attach on target riscv.cpu:

Error: Target not initialized. Return ERROR_FAIL.
Error: Target not initialized. Return ERROR_FAIL.
Error: get register list failed
Info : Failed detecting Target Description Support, disabling
warning: Remote failure reply: E01
Error: Target not initialized. Return ERROR_FAIL.
.pioinit:13: Error in sourced command file:
Could not read registers; remote failure reply 'E0E'

I attach the whole trace when debugging with -d3
swerv-openocd_full_trace.txt

Hi Alberto,
thank you for the log. What configuration file(s) do you use for OpenOCD? From the log, it seems you're using that swerv-example.cfg from the swerv-openocd repository.

Please note that this file is meant only as a crude example and needs to be modified for your own situation. Especially your JTAG adapter needs to be configured properly, as well as the JTAG chain in your target hardware.

What JTAG adapter are you using?

  • If for instance you are using Olimex ARM-USB-TINY-H adapter, you would need to delete the first section of swerv-example.cfg and replace it with source [find interface/ftdi/olimex-arm-usb-tiny-h.cfg].

  • Also update your JTAG TAP configuration to match reality (jtag newtap ...).

  • Finally, update the memory ranges where your ICCM, DCCM and PIC are mapped (riscv add_abstract_mem_range ....).

Please let me know if that helped.

Regards,
Jan

* Also update your JTAG TAP configuration to match reality (`jtag newtap ...`).

I am using a Nexys A7 board, so the JTAG is the one by Digilent. Basically I have followed swerv-example.cfg structure because it resembled a lot to the .cfg file I was using with the regular riscv-openocd debugger. Let me attach the file

swerv-nexys-debug.cfg.txt

As for the ICCM and DCCM I have used the addresses given in the PRM in Table 2-10.

Hi Alberto,
Please note that the address map shown in Table 2-10 of the PRM is just an example. The actual addresses and sizes of the ICCM and DCCM are core build arguments.

I am using a Nexys A7 board, so the JTAG is the one by Digilent.

Hi Alberto,
just as background information, it is important to understand that the JTAG interface on Digilent Nexys A7 board is the JTAG interface of the FPGA chip itself, not SweRV's JTAG port. Those are essentialy two different JTAG interfaces.

In the beginning of this thread, it is mentioned that you're using SweRVolf as your SoC, so I'm assuming that is still the case.

SweRVolf is a special case when it comes to JTAG - it uses a proprietary mechanism (Xilinx BSCAN cell) to "tunnel" the SweRV's JTAG port through the JTAG port of the FPGA chip, so it looks like you are using just one JTAG interface although in reality you're dealing with two.

For SweRVolf, please update your .cfg file by adding these lines. They are required due to the use of the Xilinx BSCAN cell:

riscv set_ir idcode 0x9
riscv set_ir dmi 0x22
riscv set_ir dtmcs 0x23

(See also the original SweRVolf's .cfg file: https://github.com/chipsalliance/Cores-SweRVolf/blob/master/data/swervolf_nexys_debug.cfg)

This should make OpenOCD work in your situation.

Feel free to ping back if run into further issues.
Jan

riscv set_ir idcode 0x9
riscv set_ir dmi 0x22
riscv set_ir dtmcs 0x23

Thanks! Now it is working, however the pending bit is still there. But at least I can see the memory content.

Thanks for reaching out, Alberto. Hope we have been able to address your issues.