NRFJPROG_rtt_ functions wait for caller thread to finish before executing in C#

Hello,

I am using nRF52840 DK's P20 SWD to communicate with an external nRF52840 board. My setup works fine with nrfjprog running from command line. To communicate with the external nRF I established RTT communication using jlinkarm_nrf52_nrfjprog.dll in C#.

I have loaded the libraries as described here.

I have successfully opened the JLink_x64.dll, connected and ask information about the board using NRFJPROG_read(). 

I have also successfully established RTT connection using NRFJPROG_rtt_start() and it finds the control block automatically (verified with NRFJPROG_rtt_is_control_block_found).

The point where I am stuck is using the RTT read write functions of the dll.

I have the following function in C#:

public override bool SendCommand(string command, int timeoutMsec, out CommandResult result)
{
    //Create command (Place for protocol design)
    StringBuilder sbTemp = new StringBuilder();
    sbTemp.Append(command);
    sbTemp.Append('\r');
    sbTemp.Append('\n');

    Thread WriteAndRead = new Thread(p => {

        Console.WriteLine("Hello!1");
        
        nrfjprog.WriteRTT(sbTemp.ToString());
        Console.WriteLine("Hello!2");

        _logCommand(command + " written to DUT");

        for (int i = 0; i < timeoutMsec; i++)
        {
            if (nrfjprog.ReadRTT(out string data) && data != string.Empty)
            {
                if (data[data.Length - 2] == '\r' && data[data.Length - 1] == '\n')
                {
                    data = data.ToString().Substring(0, data.Length - 2);
                    m_CommandResult = CommandResult.ParseResponse(data);
                    _logCommand(data + " received from DUT.");
                    return;
                }
            }
            Thread.Sleep(1);
        }
        
    });

    WriteAndRead.Start();

    Thread.Sleep(10000); //Wait for WriteAndRead to finish

    Console.WriteLine("Hello!3");

    //Wait for thread and use m_CommandResult here

    result = m_CommandResult;
    return true;
}

With the write and read rtt functions:

public bool WriteRTT(string data, int channel = 0)
{
    NRFJPROG_RESULT return_value;

    if (data == null)
        return false;

    return_value = NRFJPROG_rtt_write(channel, data, data.Length, out int data_written);                    
    
    if (return_value != NRFJPROG_RESULT.SUCCESS || data_written != data.Length)
        return false;
    else
        return true;
}

public bool ReadRTT(out string data)
{
    NRFJPROG_RESULT return_value;            

    StringBuilder sb = new StringBuilder(1024);
    return_value = NRFJPROG_rtt_read(0, sb, sb.Capacity, out int data_read);

    data = data_read > 0 ? sb.ToString() : string.Empty;

    if (return_value != NRFJPROG_RESULT.SUCCESS || data_read == 0) return false;
    else return true;
}

RTT Communication works! But I got one problem. The output from Console.WriteLine(s) are:

Hello!1
Hello!3
Hello!2

I want it to be 

Hello!1
Hello!2
Hello!3

Using the debugger, the WriteAndRead thread waits at return_value = NRFJPROG_rtt_write(channel, data, data.Length, out int data_written); until the caller thread is finished. Why? There is no information about it in the jlinkarm_nrf52_nrfjprogdll.h.  I can't use the results I want. (Yes I can use a C# handler, but then I'd have to refactor alot of code.)

I am using nrfjprog version 10.15.2.0 and JLink version 7.58.2.0

  • RTT read blocks until it reads something from the DUT. It sounds like your target device isn't sending anything up, so the rtt thread blocks until the main process exits. We assume that in the process of exiting the user calls NRFJPROG_close_dll which will abort the rtt read call, and allows Hello World 3 to be printed.

    You should structure your program around this, as that's how the API works. Unfortunately this is based on J-Link DLL behavior, and we're not able to change that. We are however introducing asynchronous APIs for RTT that handles all the threading and blocking internally to the DLL, not sure if that's any help here.

    Hope that helps,
    Kenneth

  • Hello Kenneth,

    Thanks for your swift response.

    RTT read blocks until it reads something from the DUT. It sounds like your target device isn't sending anything up, so the rtt thread blocks until the main process exits.

    It's not blocking at RTT read but at rtt_write(). I forgot to add that I am using C#'s Winforms. So when Im saying "caller thread exits", I actually mean when a UI interaction has been resolved, my apologies. I have not tested the dll's functionality on a normal c# application yet to pinpoint if the problem lies with WinForms. This is the buttonClick function. 

    private void buttonSendRTT_Click(object sender, EventArgs e)
    {
        rttCmdHandler.SendCommand("buzzer:nodisplay", out CommandResult cmdResult);
        if (cmdResult.ResType == CmdResType.OK)
            ChangeFeedbackLabel("RTT Buzzer response success", FeedbackLevel.SUCCESS);
        else
            ChangeFeedbackLabel("Failed to establish RTT Connection.", FeedbackLevel.ERROR);
    }

    It sends a simple buzzer request to the DUT. The DUT will then write a response through RTT telling the host that it has been executed (using SEGGER_RTT_*). If buttonClick ends, only then will NRFJPROG_rtt_write() continue (and thus the DUT buzzer will beep), NRFJPROG_rtt_read() reads the response correctly.

    We assume that in the process of exiting the user calls NRFJPROG_close_dll which will abort the rtt read call, and allows Hello World 3 to be printed.

    Hello!3 gets printed before Hello!2 because the WriteAndRead thread blocks at NRFJPROG_rtt_write(). This is the only location where I am calling NRFJPROG_close_dll, and it is called when the main window is closed.

    public void Close()
    {
        NRFJPROG_RESULT return_value;
    
        return_value = NRFJPROG_is_rtt_started(out bool started);
        if (started) return_value = NRFJPROG_rtt_stop();
    
        return_value = NRFJPROG_is_connected_to_device(out bool is_emu2device_connected);
        if (is_emu2device_connected) return_value = NRFJPROG_disconnect_from_device();
    
        return_value = NRFJPROG_is_connected_to_emu(out bool is_connected);
        if (is_connected) return_value = NRFJPROG_disconnect_from_emu();
    
        return_value = NRFJPROG_is_dll_open(out bool is_opened);
        if (is_opened) NRFJPROG_close_dll();
        
    }

    You should structure your program around this, as that's how the API works. Unfortunately this is based on J-Link DLL behavior, and we're not able to change that. We are however introducing asynchronous APIs for RTT that handles all the threading and blocking internally to the DLL, not sure if that's any help here.

    My goal is to actually perform synchonous RTT communication using send/receive pairs. That would only work if rtt_* functions can be called without exiting the caller thread.  Am I correct that you are implying that this is not possible? I just want to make sure I am not chasing impossibilities.

    My assumption why NRFJPROG_rtt_write() blocks would be that the WinForm is locking the dll until its UI action has been resolved. But I have not confirmed this yet, nor would I know how to solve the problem.

Related