Hello everyone, I'm experiencing some issues with ...
# caravel
j
Hello everyone, I'm experiencing some issues with verification using cocotb, I have attached a FIFO to the wishbone bus and am trying to run the following test attached, however when i run it crashes with 931544.00ns INFO cocotb [UART] line recieved = 0x. So it's not receiving the value. If I set the data variable to say 123 and then send it, it works, but when I read the fifo it does not. The waveform however, shows correct behaviour of the wishbone bus. What is going wrong?
Here is the waveform
And the python file for the test
r
@Anton Maurovic (efabless support) the waveform looks to me like a proper write / read cycle on the wishbone bus but it seems that the data read from the bus doesn't make it into the cocotb output. Do you have any suggestion as to what may be going wrong here or who could be of help in debugging cocotb issues?
a
Hi @Roel Jordans and @Jelmer Lap I can't yet spot what might be wrong. To confirm, you replaced this line:
Copy code
uint32_t data = USER_readWord(0);
...with this:
Copy code
uint32_t data = 123;
And then your test correctly produced this output:
Copy code
[TEST] read 123
...do I understand that correctly?
By the way, I'm not sure, but it looks wrong to me that there is a 0 value in your waveform here:
It sort of looks like your Wishbone slave is asserting this value on
wbs_dat_o
as it completes the transaction. I'm unsure if that's right or not. Are you maybe "draining" your FIFO on the wrong clock cycle, and
0x0000DEAD
should carry over for 1 more clock? I actually don't know in a Wishbone read whether that's required or not.
I think from my reading, the WB master will sample
wbs_dat_o
on the first clock after the ACK pulse. If that's true, it is sampling 0, not 0xDEAD, and therefore you'll need to find out why your FIFO is asserting 0... my guess is you need to make the output 1 more register-delay deep.
I think if you look at this reference, it's a little confusing especially compared with subtle differences we see in the waveform from Caravel, but I think the essential part is that the
wbs_dat_o
value presented by your slave is being latched by Caravel in this region (which is when the value is 0, instead of 0xDEAD):
Note that these signal directions in the diagram are written from the point-of-view of the master... while our signal names are from the point-of-view of the slave.
j
@Anton Maurovic (efabless support) You understand correctly how the test works with interchanging the line for a hardcoded value.
There is one thing I tested after posting this that seems to refute your theory about why it is not latching the data properly: I assert 0's in the fifo when the slave is not selected anymore. From the specification you send we can see this is when the masters STB and CYC go low, as after that the data is not valid anymore. In the waveform, the ack responds to the STB and CYC going low and I remove my data from the bus after the ack is lowered (should've been verilog z's but is in this test 0's, the z's do not make a difference in the result of the test)
I have tested as well with asserting a 0xDEAD on the bus for the entire duration of the address matching but this didn't seem to change either. I can get those waveforms to you in a bit
Here is the waveform for holding the data (this time the data is 0xABBA) for longer and where I changed the fifo to high impedence when not selected. Still the same issue persists
a
Hi @Jelmer Lap and @Roel Jordans, are you able to push your repository (even just a minimal example) up to the Efabless repo servers and give me the repo link so I can try to reproduce your findings? Alternatively, can you make it available via (say) a GitHub repo?
j
@Anton Maurovic (efabless support) Here is a link https://github.com/JJellie/caravel_user_project_2
a
Thanks @Jelmer Lap. Could you confirm for me what command you're using to run your RTL and GL tests? I am assuming you are running verilog/dv/cocotb/wishbone_nn_test/test1/
j
I am using caravels
make cocotb-verify-test1-rtl
and
make cocotb-verify-test1-gl
@Anton Maurovic (efabless support)
a
Hi @Jelmer Lap, I suspect your test is working, but there is a bug in caravel_cocotb's
UART.get_int
because the actual error message is:
Copy code
msg = await uart.get_int()
  File "/usr/local/lib/python3.10/dist-packages/caravel_cocotb/interfaces/UART.py", line 49, in get_int
    return int(line, 16)
ValueError: invalid literal for int() with base 16: ''
I will try to look into this more, and see if I can offer a workaround until this can be fixed (if it is in fact a caravel_cocotb bug)
Actually on closer inspection, it's more nuanced... the firmware function
UART_sendInt
will take an int (supporting up to 32 bits) and attempt to send it as a stream of hex characters per each nibble, starting with the lowest nibble first (which means it transmits it in reverse order), and ending with a newline (
'\n'
, i.e. ASCII 0x0A) Thus, if you call
UART_sendInt(0x1234ABCD)
then the literal string it sends over UART will be:
"DCBA4321\n"
-- The caravel_cocotb function UART.get_int reverses this and converts it back to an integer value. The problem you're hitting is that the
UART_sendInt
function attempts to drop leading zeroes, but if the value itself is 0, then it will send no digits... it'll just send a newline. In this case, UART.get_int attempts to interpret an empty string as though it is a hex integer. So, aside from that issue, we need to work out why your
USER_readWord()
is returning a 0.
I feel like it's a C compiler optimization error, because this code fails in the same way:
Copy code
#include <firmware_apis.h> // include required APIs

void main(){
    ManagmentGpio_outputEnable();
    ManagmentGpio_write(0);
    enableHkSpi(0); // disable housekeeping spi
    // configure all gpios as  user out then chenge gpios from 32 to 37 before loading this configurations
    GPIOs_configureAll(GPIO_MODE_MGMT_STD_OUTPUT);
    GPIOs_loadConfigs(); // load the configuration 
    
    // Enable UART
    UART_enableTX(1);
    
    User_enableIF();
    ManagmentGpio_write(1); // configuration finished 
    // writing to any address inside user project address space would reload the counter value
    
    // In theory: Write to fifo, then read result
    reg_mprj_datal = 0x5555AAAA;
    USER_writeWord(0xABCDEF00,0);
    uint32_t data = *((volatile uint32_t*)(0x30000000));
    //reg_mprj_datal = data;
    UART_sendInt(data);
    
    return;
}
...but if I uncomment the
//reg_mprj_datal = data;
line, it works as expected (i.e. correct data is presented at GPIOs, and correct data is sent via UART).
My understanding is that the use of
volatile
everywhere should not allow this sort of optimization problem to happen. I haven't tried looking into the assembly to see if that's what's really happening, though.
On review, @Jelmer Lap, the correct thing to do is to declare your
data
variable as
volatile
also, i.e.
Copy code
volatile uint32_t data = USER_readWord(0);
j
@Anton Maurovic (efabless support) 🤦. I am almost sure I tested this but apparently not because then I would've solved this on my own. Thank you for taking so much of your time to help me out.
a
@Jelmer Lap No problem! We learn together. If you happen to retest this with your original Verilog that outputs 0 after ACK, please let me know if that still works, as that would correct my original assumption about the sampling time.
j
@Anton Maurovic (efabless support) I have retested it such that it immediately puts high impedance when the ack goes low and it still works, so the user project satisfies to the wishbone spec fully.
a
Sorry for the delay @Jelmer Lap but that's great to hear. How are you coming along otherwise? Do you need any further assistance?
j
@Anton Maurovic (efabless support) don't worry about the delay. Together with Mitch Bailey we managed to fully place our custom gds blob in the user project and the next step is routing all the wires where they need to go. Currently that is running quite smoothly so we're all good here!
@Anton Maurovic (efabless support) If the offer still stands, I could use some extra assistance. We are wondering what the procedure is for setting the GPIO pads in analog mode for connection to our macro. Could you explain what settings I need to set?
And another question @Anton Maurovic (efabless support) when using the user interrupts. Can I write an interrupt handler for this in cocotb?
void IRQ_enableUser0(bool is_enable)
allows us to enable it in cocotb, but how do we write firmware that reacts to the user interrupt?
a
Hi @Jelmer Lap : 1. @Mitch Bailey might be able to give the best advice on configuring GPIOs for analog connections (in case I get it wrong) but from what I understand you must set
io_oeb
high (to disable output buffers), leave
io_out
disconnected, and configure the GPIO with mode GPIO_MODE_USER_STD_ANALOG -- ideally in
verilog/rtl/user_defines.v
and without ever changing it in firmware (since you probably wouldn't want to risk contention where a digital output gets enabled and shorts) -- though if you're careful there are ways you could design a solution that handles and even takes advantage of this. 2. I actually don't yet know the correct/preferred way to set up an ISR for IRQs, but I can offer the following advice: RISC-V has an "mtvec" register which is a convention for interrupt/exception addresses (but behaves more like a singular or vectored 'jump' table rather than an address table). Also, if you want a FAST ISR, you would want to make sure both the ISR, and address that mtvec points to, are located entirely in local RAM (i.e. within the limited 1.5kBytes that Caravel has on-chip, or otherwise extend RAM via the Wishbone bus) -- otherwise, when the interrupt triggers, it could take 1,000-2,000 cycles to be serviced because of the flash firmware SPI cache miss. As for an ISR from cocotb... do you mean Python code that runs in response to an IRQ? Or do you just mean supporting a C code ISR via the same cocotb infrastructure (which WILL work).
m
@Jelmer Lap I recommend always connecting
io_oeb
and
io_out
. With
GPIO_MODE_USER_STD_ANALOG
it does not matter whether they are high or low.
a
@Jelmer Lap I feel like you will find the answer to what you want re IRQs/ISRs somewhere in the API source & example files in this directory: https://github.com/efabless/caravel_board/tree/5b44f382b1276531095d4cdf9c8bc0e7a19183f8/firmware/chipignite
@Jelmer Lap our internal Caravel team have given me some advice. I believe we do have what is maybe a default very generic ISR which -- at the C level -- is isr(). It looks like maybe it gets modified as you like in your own local copy of these files, rather than being a more formal multi-purpose ISR or API structure. In a way this makes sense, given the constrained nature of the Caravel SoC memory, instruction cache, and relatively slow SPI access; it's closer to bare-metal than you might see with larger microcontrollers. However, there are potentially at least 2 levels below that which you might be interested in. There's this line which I believe calls the higher-level isr()... and that is within a lower-level assembly ISR entrypoint that is probably configured via the RISC-V
mtvec
register.