Ripes icon indicating copy to clipboard operation
Ripes copied to clipboard

External io tcp support

Open lcgamboa opened this issue 2 years ago • 12 comments

Initial release of IPC support. I only tested on Linux. After unraveling the obscure MSVC compiler error messages the code compiles in github actions. But I don't have MSVC available to test here.
The server for testing is on another branch, I don't think it's interesting to add it to the main Ripes version.

lcgamboa avatar Nov 11 '21 00:11 lcgamboa

The server for testing is on another branch, I don't think it's interesting to add it to the main Ripes version.

I think since it seems like we're going to go with a non-Qt solution for the tcp client, i think it would be good to have proper tests of the solution so we can verify it on all platforms. I imagine a new tst_xtcpsocket.cpp test which could use this as a dummy server.

mortbopet avatar Nov 11 '21 08:11 mortbopet

@lcgamboa could you elaborate a little bit on what/where crashes are triggered?

mortbopet avatar Dec 12 '21 08:12 mortbopet

I made all the modifications you requested. Sorry for the lack of comments in the source code, it really is my fault for not being used to working in a team. As for errors, they do not occur in normal operation. A simple way to test is to close the connection during Ripes' No-GUI simulation. To do this, just press the reset button on the PICSImLab board which resets the connection. When the connection error occurs sometimes Ripes suffers a segmention fault instead of just showing the error message.

lcgamboa avatar Dec 13 '21 01:12 lcgamboa

I made all the modifications you requested. Sorry for the lack of comments in the source code, it really is my fault for not being used to working in a team.

Thanks! don't worry too much about it, it's just another benefit of doing collaborative open source work that the people involved gain experience in giving, getting, and acting on feedback!

A simple way to test is to close the connection during Ripes' No-GUI simulation. To do this, just press the reset button on the PICSImLab board which resets the connection. When the connection error occurs sometimes Ripes suffers a segmention fault instead of just showing the error message.

I will try this out once i have the chance :+1:. I would also recommend rebasing your branch to master since there was a bit of a nasty bug in the InstructionMatcher which caused some issues.

mortbopet avatar Dec 13 '21 07:12 mortbopet

Yes I have learned a lot working on Ripes. As for the rebase, as my branch is based on your external_io branch, I believe you have to rebase it first in relation to the master.

lcgamboa avatar Dec 13 '21 17:12 lcgamboa

As for the rebase, as my branch is based on your external_io branch, I believe you have to rebase it first in relation to the master.

You're right - should be done now :+1: .

mortbopet avatar Dec 13 '21 19:12 mortbopet

Would you mind doing a rebase onto the current master branch? I think you've merged the master branch into this branch. By doing this, all changes in the master branch is also seen as changes in this PR, making it hard to review the code (i cannot discern your contributions from the additions in the master branch).

mortbopet avatar Mar 04 '22 08:03 mortbopet

I added support for passing simulation time to PICSimLab to synchronize simulators. Is there an easy way to be able to use the instruction counter value instead of the constants I added for testing?

https://github.com/lcgamboa/Ripes/blob/c3a4e5ca0031db5711a7cbded3e7378d6fb25320/src/io/ioexternalbus.cpp#L54

lcgamboa avatar May 01 '23 17:05 lcgamboa

@lcgamboa the processor interface contains a hook to get the current cycle count, which can be used through the processor handler.

mortbopet avatar May 05 '23 09:05 mortbopet

@mortbopet I ended up using the wall time instead of the instructions counter, it got better result since Ripes doesn't use a fixed simulation frequency.

I followed your guidance and tried to use QTcpSocket instead of the XTcpSocket class I created. But I keep having deadlock problems because of Qt's sockets implementation and the ioRead and ioWrite implementation in Ripes. Running one cycle at a time works fine. Running in animation mode with an interval greater than 150ms works without problems, with smaller intervals occasionally deadlock occurs as several threads try to use the ioRead and ioWrite methods simultaneously. In no-gui fast mode it works until you try to pause the simulation. When the pause is triggered several threads try to use the ioRead method at the same time and deadlock always occurs.

As it is necessary to send a command via TCP and wait for the response within the ioRead and ioWrite methods, the only possible way I found to wait is to use a QEventLoop. To solve the problem, it would be necessary to manage at a higher level so that there was no access to the ioRead and ioWrite methods by several threads simultaneously or change the form of request of the ioRead and ioWrite methods to generate a trigger signal and wait for another signal response. I don't have enough Ripes knowledge to make that kind of change and not whether the work would pay off. Maybe using XTcpSocket isn't such a bad idea for now.

If you want to take a look at the code using QTcpSocket it's in this branch https://github.com/lcgamboa/Ripes/blob/QTtcpSocket/src/io/ioexternalbus.cpp .

lcgamboa avatar May 06 '23 02:05 lcgamboa

But I keep having deadlock problems because of Qt's sockets implementation and the ioRead and ioWrite implementation in Ripes.

Could you explain this a bit more? I'm assuming that the control flow of a simulation is:

  1. code executes in ripes
  2. an MMIO-mapped access is [read | written] to the external bus
  3. this feeds into ioWrite/ioRead functions
  4. This creates a tcp message which eventually will return - the Ripes simulation is stalled in the ioWrite/ioRead command while this is going on.
  5. The TCP request finishes, and the simulation progresses.

Where does the deadlock come into play? where are the multiple threads coming from/doing, which tries to access the bus concurrently?

mortbopet avatar May 06 '23 07:05 mortbopet

Apparently the problem is between step 4 and 5, the Ripes simulation is not always stalled.

Running step by step everything works, several threads access the ioRead and ioWrite methods.

ioRead  start threadID= QThread(0x555556124120)  simtime=  1683376517233706956
ioRead  stop  threadID= QThread(0x555556124120)  simtime=  1683376517233706956
ioRead  start threadID= QThreadPoolThread(0x5555572a6d90, name = "Thread (pooled)")  simtime=  1683376532943357397
ioRead  stop  threadID= QThreadPoolThread(0x5555572a6d90, name = "Thread (pooled)")  simtime=  1683376532943357397
ioRead  start threadID= QThreadPoolThread(0x55555675eb30, name = "Thread (pooled)")  simtime=  1683376533556835921
ioRead  stop  threadID= QThreadPoolThread(0x55555675eb30, name = "Thread (pooled)")  simtime=  1683376533556835921
ioRead  start threadID= QThreadPoolThread(0x5555572b7ab0, name = "Thread (pooled)")  simtime=  1683376535504702982
ioRead  stop  threadID= QThreadPoolThread(0x5555572b7ab0, name = "Thread (pooled)")  simtime=  1683376535504702982
ioRead  start threadID= QThread(0x555556124120)  simtime=  1683376535561063213
ioRead  stop  threadID= QThread(0x555556124120)  simtime=  1683376535561063213

In animation mode apparently the main thread tries to use ioWrite before the pooled thread finishes ioRead:

ioRead  start threadID= QThread(0x555556124120)  simtime=  1683376663470320350
ioRead  stop  threadID= QThread(0x555556124120)  simtime=  1683376663470320350
ioRead  start threadID= QThreadPoolThread(0x5555572ac290, name = "Thread (pooled)")  simtime=  1683376665338831463
ioRead  stop  threadID= QThreadPoolThread(0x5555572ac290, name = "Thread (pooled)")  simtime=  1683376665338831463
ioRead  start threadID= QThreadPoolThread(0x55555725f150, name = "Thread (pooled)")  simtime=  1683376665373060131
ioRead  stop  threadID= QThreadPoolThread(0x55555725f150, name = "Thread (pooled)")  simtime=  1683376665373060131
ioRead  start threadID= QThreadPoolThread(0x5555571802a0, name = "Thread (pooled)")  simtime=  1683376665438149355
ioRead  stop  threadID= QThreadPoolThread(0x5555571802a0, name = "Thread (pooled)")  simtime=  1683376665438149355
ioRead  start threadID= QThread(0x555556124120)  simtime=  1683376665472576233
ioRead  start threadID= QThreadPoolThread(0x555556f71830, name = "Thread (pooled)")  simtime=  1683376665478719220
ioRead  stop  threadID= QThread(0x555556124120)  simtime=  1683376665472576233
ioRead  start threadID= QThread(0x555556124120)  simtime=  1683376665487467718
ioWrite start threadID= QThreadPoolThread(0x555556f71830, name = "Thread (pooled)")  simtime=  1683376701053311357
"Unknown error"
ioRead  stop  threadID= QThread(0x555556124120)  simtime=  1683376665487467718
QIODevice::write (QTcpSocket): device not open

In fast simulation mode, when the pause is triggered apparently the main thread tries to access the ioRead several times.

ioWrite start threadID= QThreadPoolThread(0x55eef4307fe0, name = "Thread (pooled)")  simtime=  1683376957597663202
ioWrite stop  threadID= QThreadPoolThread(0x55eef4307fe0, name = "Thread (pooled)")  simtime=  1683376957597663202
ioWrite start threadID= QThreadPoolThread(0x55eef4307fe0, name = "Thread (pooled)")  simtime=  1683376957598503058
ioWrite stop  threadID= QThreadPoolThread(0x55eef4307fe0, name = "Thread (pooled)")  simtime=  1683376957598503058
ioRead  start threadID= QThreadPoolThread(0x55eef4307fe0, name = "Thread (pooled)")  simtime=  1683376957598720687
ioRead  start threadID= QThread(0x55eef328c120)  simtime=  1683376967598299545
ioRead  start threadID= QThread(0x55eef328c120)  simtime=  1683376967682604900
ioRead  stop  threadID= QThread(0x55eef328c120)  simtime=  1683376967682604900
ioRead  start threadID= QThread(0x55eef328c120)  simtime=  1683376967732737434
ioRead  stop  threadID= QThread(0x55eef328c120)  simtime=  1683376967732737434
ioRead  start threadID= QThread(0x55eef328c120)  simtime=  1683376967762770341
ioRead  stop  threadID= QThread(0x55eef328c120)  simtime=  1683376967762770341
ioRead  start threadID= QThread(0x55eef328c120)  simtime=  1683376967779625643
ioRead  stop  threadID= QThread(0x55eef328c120)  simtime=  1683376967779625643
ioRead  stop  threadID= QThread(0x55eef328c120)  simtime=  1683376967598299545
Falha de segmentação

To wait for the Socket's response, it is necessary to use a local QEventLoop to process the signals and events, I believe that this is why the simulation does not stalled.

lcgamboa avatar May 06 '23 12:05 lcgamboa