This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Dynamic reconfiguration of peripheral HW from TWI to SPI

Our nRF52840 is communicating with a peripheral chip (let's call it FOO) through an SPI interface. The FOO chip can be configured to use either TWI or SPI on the same pins (well TWI uses a subset of the SPI pins), the default mode after FOO reset is TWI and a register TWI_DISABLE in FOO has to be configured to ensure that TWI does not risk to spoil SPI by accident.

We need SPI, because the TWI does not have sufficient throughput to use all of the features of the FOO chip.

When the FOO chip starts up, the TWI_DISABLE is not set, so FOO is in a sort of blind detection mode in which it will accept both SPI and TWI at the same time. In this blind mode, the TWI cannot be disturbed by the SPI, because the SPI needs chip select (CS) pin active, and the CS pin default setting is inactive when in TWI mode. However, the converse statement is not true : theoretically it is possible that bad luck causes that you accidentally have TWI acting while in SPI and make the SPI link fail. 

It is possible after startup of FOO to make a loop so that, while communicating in SPI, you write the TWI_DISABLE register and check it by reading it afterwards until TWI_DISABLE is really set, and then you are OK. We checked this solution and it is working fine to do so and seems to be the standard approach.

However that sounds like a dirty solution that we cannot guarantee will always in all circumstances and over millions of items work. We would like to make it really bulletproof and another solution is to start to communicating with FOO over TWI, write the TWI disable register of FOO so that the TWI is disabled in FOO, and then we are OK too for using SPI safely.

We checked this solution too with some quick and dirty test FW and it works fine.  However it is not possible to make it easily with the Nordic SDK 15 because the SDK ensures at compile time that you cannot use the same HW peripheral in TWI and in SPI. Say for instance that I want to use SPI0 to communicate with FOO, then I cannot enable TWI0 too, even though there would not be any conflict because the FW would use TWI0 only for a short time at the beginning just to set TWI_DISABLE FOO register and then be uninit'ed.

It is not possible for us to use TWI1 instead of TWI0 because all the HW ressources of nRF52840 are taken in our application, so if we go to the bulletproof solution we really need to reconfigure dynamically (at runtime, not at compile time) the same nRF52840 peripheral. The nRF52840 HW allows this, but this is a limitation of the SDK that makes it impossible. This limitation is really quite usefully and a very good idea for most of the cases because it prevents people from having HW conflicts in their design, however, in our very specific corner case it is preventing us to make it.

Your support is most welcome…

  • To be more complete, I also printed the transfer descriptor, it is as follows (I just obfuscated the device address):

    (gdb) print /x *p_xfer_desc
    print /x *p_xfer_desc
    $6 = {type = 0x2, address = 0x…, primary_length = 0x1, secondary_length = 0x1, p_primary_buf = 0x200041e3,
    p_secondary_buf = 0x200041e0}

  • Ooops… SOMETHING has happened, it has worked !

    I must admit that it is not exactly the same FW as that on which I observed the issue when I opened the thread, I will try to make a git clone of the same version to see if I can reproduce the problem again…

  • OK, I was a bit too quick in writing that it had worked. In fact there is exactly the same problem in the same conditions.

    When I run the program with this sort of command line:

    nrfjprog --reset && JLinkRTTLogger -Device nrf52840_XXAA -If SWD -Speed 4000 -RTTCHannel 0 /dev/stdout

    then I get the problem occurring (ie my FreeRTOS queue is timed out, which calls the app error handler).

    We are using SEGGER backend for the NRF LOGGER, and it seems that this is what creates the condition that makes the problem happen.

    When I run the same program under the debugger (which is what I did to post the content of registers as you required), then I can get through the line at which the problem occurs without it occurring --- that is why I thought in my previous post that the problem had disappeared.

    In fact it is still there but I was not in the conditions in which it happens.

    Maybe I should push the TWI registers content to the logger so that we can see what is their content in the conditions when the problem happens.

  • We need the content of the registers before the TASKS_STARTTX is triggered, but after the driver has set up the transfer.

  • Hello,

    I have done some experiments, and I realized a few things.

    First, as you remember I had a version of the FW, let us call it V0, in which I had no callback neither in running the program freely with just RTT logging, nor in debugging session, and a second version, let us call it V1, in which I had the callback when running in debugger but not when running the SW freely with just RTT logging.

    So the first thing which I have understood is that in V0 there was a mistake of mine by which the NRFX_PRS_BOX_0_ENABLED, and NRFX_TWI0_ENABLED and TWI0_ENABLED flags were not dispatched to all the FW but only to the part which was calling and configuring the TWI0. This mistake may explain the problem, but not fully because when I placed some « #error Voilà » in the nrfx_twi.c code under condition of these flags I could observe that nrfx_twi.c had the flags.

    Anyway, the problem still exists with V1, and that seems to be some real time issue in the way the TWI0 is configured. Probably there should be some « nrf_delay_us(…) » just before the « nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTTX) » possibly depending on the TWI configured speed.

    As I could not see the registers under the debugger, because I cannot reproduce the bug this way, what I did is place some « memcpy(temp_twi_register_state,p_twi,sizeof(NRF_TWI_Type)); » just before the « nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTTX) ». Doing this created some delay before « nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTTX) » and that sufficed to make it work (ie that caused the call back to be called).

    For your information, here is the register state dump:

    <info> FOO: twi register state = [1420]:

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0100000000000000000000000100000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000001000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0100000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 05000000000000001b00000008000000ffffffffffffffff0000000000000000

    <info> FOO: 0000000000006806000000000000000000000000000000000000000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000200000000000000

    <info> FOO: 0000000000000000000000000000000000000000000000000000000000000000

    <info> FOO: 010000000000000069000000

Related