#22029 - Gnurou - Fri Jun 11, 2004 4:49 pm
Hello everyone,
I've been browsing many sources (including this forum), but unfortunately I can't get it to work the way it should. Any advice would be greatly appreciated here. Some words about the context first:
I'm working on a Java-OS that supports the GBA. We implemented a TCP/IP stack in Java that allows nice things, like hosting a real HTTP server on the GBA that gets its internet connection from the serial line via a PC. Unfortunately, I'm losing too many bytes on the physical layer, because I don't manage to get the flow control to work the way I want.
The VM can run multiple threads, and threads waiting for serial data should be put into an idle list and awakened when the data actually arrived. So I'm naturally using an interrupt-based approach for handling data arrival. Once the interrupt is triggered, the byte is put into a buffer (that is large enough to contain all the bytes I send during a session for my tests). Here is the serial initialization code:
Code: |
REG_SIODATA8 = 'A';
REG_SIOCNT = 0;
REG_RCNT = RCNT_MODE_UART;
REG_SIOCNT |= SIO_BAUD_9600 | SIO_CTS | SIO_LENGTH_8 | SIO_USE_UART;
REG_SIOCNT |= SIO_SEND_ENABLE | SIO_RECV_ENABLE;
REG_SIOCNT |= SIO_SEND_DATA | SIO_RECV_DATA;
REG_SIOCNT |= SIO_REQUEST_IRQ;
|
And here is the interrupt code:
Code: |
u32 Int_Flag;
/* Disable interrupts */
Int_Flag = REG_IF;
REG_IME = 0x00;
/* Only act when we receive a character */
if (!(REG_SIOCNT & SIO_RECV_DATA))
{
/* Code to store the character (slightly longer in real) */
buffer[cpt++] = (char)REG_SIODATA8;
REG_SIOCNT |= SIO_SEND_DATA | SIO_RECV_DATA;
}
REG_IF = Int_Flag; /*Write back the interrupt flags */
BIOS_IF |= Int_Flag;
REG_IME = 0x1; /* Re-Enable interrups */
|
Using this code I'm losing something like 5% of the transmitted characters. I'm kinda desesperate since 1) the desired interrupt code would be quite long in some cases (in case you need to reschedule the thread receiving the byte) and 2) I don't manage to get CTS/RTS flow control to work, despite of a good hundred tests. The idea is to tell the sender to stop sending bytes at the beginning of the interrupt code, than re-enable sending at the end.
Has anyone managed to get something like this to work? Can you see something wrong in my code that would explain why I'm losing characters? My test protocol is a simple "cat file.txt > /dev/ttyS0" under Linux, with the serial line well configure. The Java program on the GBA reads the bytes as they arrive and display them on the screen. This process should not interfere with characters receiving since latter is under interruption and only place them into a buffer.
Thanks in advance for your help,
Alex.
#22030 - poslundc - Fri Jun 11, 2004 4:59 pm
I'd be lying if I said this was much more than a shot in the dark here, but there is a chance that you may be missing some interrupts if they are hitting while your ISR is processing.
Instead of turning off REG_IME, try disabling the specific interrupts in REG_IE instead, which will hopefully allow interrupts to still queue up.
Again, it's a bit of a blind shot, but it may help.
Dan.
#22037 - torne - Fri Jun 11, 2004 7:34 pm
Interrupts should still be queued even with REG_IME disabled; only disabling the appropriate bits in the serial registers should actually disable queueing.
#22038 - poslundc - Fri Jun 11, 2004 7:48 pm
Yeah, that's what I figured... :P
Sorry, no help then...
Dan.
#22048 - JasperW - Sat Jun 12, 2004 9:48 am
I've done some serial programming. A couple of points that might be of interest to you:
1) What kind of serial cable are you using? Jeff's MBV2 doesn't support CTS/RTS in hardware so you need some sort of software flow control. Darthfader's serial UART cable does but the cable I built didn't work reliably above 9600 baud. Though this might be due to not hacking up an official Nintendo cable.
2) You could turn on the FIFO buffer so your interrupt handler gets called less giving you more time to handle the interrupt. Beware though that it then only triggers after four bytes so if your data isn't dword aligned you'll have to check periodically for extra data.
Hope this helps. Good luck.
Jasper
#22049 - Gnurou - Sat Jun 12, 2004 10:08 am
JasperW wrote: |
I've done some serial programming. A couple of points that might be of interest to you:
1) What kind of serial cable are you using? Jeff's MBV2 doesn't support CTS/RTS in hardware so you need some sort of software flow control. Darthfader's serial UART cable does but the cable I built didn't work reliably above 9600 baud. Though this might be due to not hacking up an official Nintendo cable.
|
The cable has all the wires connected (i.e. including the CTS and RTS ones) and has been tested without interrupts at 115000 bauds. So I suppose it works.
Quote: |
2) You could turn on the FIFO buffer so your interrupt handler gets called less giving you more time to handle the interrupt. Beware though that it then only triggers after four bytes so if your data isn't dword aligned you'll have to check periodically for extra data.
|
I'm trying to avoid the hassle of the FIFO by using flow control, but maybe I just did it wrong. Some sources claim that the GBA UART doesn't support CTS/RTS at all. Others give informations about it, but I don't know the exact way to set/reset the CTS and RTS lines. Any information on this would help.
I'm trying to do the same using the smallest possible C program (i.e. without my VM). It will be easier to debug and I should be able to provide more informations.
Anyway, thanks already for all your answers! :)
#22050 - JasperW - Sat Jun 12, 2004 10:36 am
From my experience CTS/RTS works just fine on the GBA and you don't need to do anything other than setting it when initializing the UART. When you then set the SIO_RECV_ENABLE flag the GBA will output lo when the buffer is empty (just been read) and hi when the buffer is full (needs to be read) on the CTS line. Do you have hardware flow control enabled on the PC side? I'd suggest sending 20 bytes on the PC, reading 10 on the GBA, stop a while and read the next 10 and see if they all get through. They should if everything properly configured because the PC will halt it's transfer.
In your code you set the SIO_SEND_DATA and SIO_RECV_DATA flags everytime you read a byte but this isn't necessary. They might be writeable but you should only read them to check if the GBA is done with receiving / sending.
If SIO_RECV_DATA is lo (0) there's data, if it's hi (1) there's no data in the buffer.
Similar with SIO_SEND_DATA: if it's lo (0) you can put a character in the send buffer, if it's hi (1) it's busy sending.
Jasper
#22059 - Gnurou - Sat Jun 12, 2004 7:20 pm
Thanks for the informations Jasper. Unfortunately I'm still losing chars. I have tried the simplest possible example, a raw sending of characters without using interrupts.
Initialization:
Code: |
REG_SIODATA8 = 'A';
REG_SIOCNT = 0;
REG_RCNT = RCNT_MODE_UART;
REG_SIOCNT |= SIO_BAUD_9600 | SIO_CTS | SIO_LENGTH_8 | SIO_USE_UART;
REG_SIOCNT |= SIO_SEND_ENABLE | SIO_RECV_ENABLE;
REG_SIOCNT |= SIO_RECV_DATA | SIO_SEND_DATA;
|
Code:
Code: |
char buf[2048];
char c;
int cpt = 0;
while(1)
{
/* Wait for a char to be available on the line */
while (REG_SIOCNT & SIO_RECV_DATA);
/* Store it into the buffer */
buf[cpt++] = (char)REG_SIODATA8;
}
|
Doing this simple stuff, I'm not losing a single character. That's a relief!
However, if I add some delay after the 10th character as you suggested:
Code: |
while(1)
{
/* Wait for a char to be available on the line */
while (REG_SIOCNT & SIO_RECV_DATA);
/* Store it into the buffer */
buf[cpt++] = (char)REG_SIODATA8;
/* Delay after 10 characters */
if (cpt == 10) for (i = 0; i < 100000; i++);
}
|
Then I'm losing 5 chars after the 11th. It looks like character 11 remains in SIODATA8 until read but that the other characters are just sent blindly by the PC.
I've double-checked my cable and settings - my cable has been built using Darthfader's plans. The serial port on the PC is configured at 9600 bauds, 8 data bits, no parity, and RTS/CTS enabled. I send the 20 characters using a dumb 'echo':
$ echo -n -e "AZERTYUIOPQSDFGHJKLM" > /dev/ttyS0
And here is what I get on the GBA when I print my buffer:
AZERTYUIOPQJKLM
The delay introduced by the empty loop seems to be something like 200 ms. I thought it could be a bit much, but even if I lower the value a lot, I lose characters.
In the code above, I simply *never* control the RTS/CTS lines. I assumed this is done automatically by some action, maybe reading SIODATA_8, but even if I move the loop before reading the register, the result is the same. The PC constantly sends data to the GBA. The ideal way to code this to me would be something like:
Code: |
while (REG_SIOCNT & SIO_RECV_DATA);
/* Set CTS to lo - PC stop sending bytes */
REG_SOMETHING ^= CTS;
buf[cpt++] = (char)REG_SIODATA8;
if (cpt == 10) for (i = 0; i < 100000; i++);
/* Set CTS to hi - PC can send again */
REG_SOMETHING |= CTS;
|
I'm quite clueless as to how to achieve this. I would just be *very* nice if someone else could run the same test on his hardware to check. The reason why I was losing characters while using interruptions seems to be bad flow control. Any more infos, hints, ideas, advices, tests to run?
Thanks,
Alex.
#22060 - Gnurou - Sat Jun 12, 2004 8:19 pm
Gnurou wrote: |
The ideal way to code this to me would be something like:
Code: |
while (REG_SIOCNT & SIO_RECV_DATA);
/* Set CTS to lo - PC stop sending bytes */
REG_SOMETHING ^= CTS;
buf[cpt++] = (char)REG_SIODATA8;
if (cpt == 10) for (i = 0; i < 100000; i++);
/* Set CTS to hi - PC can send again */
REG_SOMETHING |= CTS;
|
|
Actually I've already tried something like that by disabling the RECV_ENABLE flag of REG_SIOCNT once a character arrives and re-enabling it once processing is over - but if I do so, then I only get garbage and lose even more chars. Sad, it would have been the right trick for me.
On some documentation I read:
Quote: |
The error flag is set when a bad stop bit has been received (stop bit must be 0), when a parity error has occured (if enabled), or when new data has been completely received while the receive data register (or receive FIFO) is already full.
|
But how can the GBA know that the receive data register is full, since we never acknowledge when we read it?
#22062 - JasperW - Sat Jun 12, 2004 8:54 pm
I'm almost sure the GBA handles the CTS/RTS itself and you don't need to do anything. I can't test it right now because I don't have my serial cable here but I will try it after the weekend if you're still having problems.
The only thing I can think of is a faulty cable but since you're using Darthfader's cable it should work. To test the cable write one GBA program that keeps reading data and use a volt meter to check if the line is indeed low. Then write a program that only receives one char, send a bunch from the PC and check if the line remains high.
Lastly I'm stating the obvious but you never know :) Did you declare all the SIO registers as volatile? Not doing so can give weird results but you probably already knew that.
As to how the GBA know you're reading and writing: I think the UART hardware is memory mapped to the GBA's memory and this hardware checks if data is requested and adjusts the register. But this is just a shot in the dark, I'm no hardware guru. I'm sure however it detects when you read or write REG_SIO_DATA8 because the flags SIO_RECV_DATA & SIO_SEND_DATA change (you can test this yourself).
Anyway I'm sorry I can't exactly pinpoint the problem because I don't have my cable with me at the moment but I still hope this helps. Good luck!
Jasper
#22064 - Gnurou - Sat Jun 12, 2004 10:09 pm
Hi again Jasper,
First, thanks a lot for all your involvment to this problem. I really appreciate it.
About the obvious: yes, all SIO variables are declared as volatile. :) At this stage, it's not silly to check it anyway.
About the cable, I have another one built on the same scheme at the lab, I could test with it Monday. I'm like a chicken who found a knife with a volt meter, but I can try that too. :)
Some good news now: I've found an explanation to my randomly-lost characters. The timer0 interrupt was set, and for some reason I don't know it was responsible for some chars not arriving when receiving them with the serial interrupt handler. I'll investigate what's wrong with it (aren't interrupts supposed to be queued anyway?), but I temporarily disabled it and now I can receive a full text file using interrupts.
One problem remains still, and this is where control-flow is welcome. I had success because my interrupt only put the received character into a (large) buffer. But if it takes more time (like printing on the screen for instance), then I'm losing them again. I added support for the ERROR flag of SIOCNT in my interrupt handler and it effectively signals me when I'm losing characters. On a receive-and-print test, I lose 3 characters every time the terminal scrolls (since it involves some longer process). This is constant - no more, no less, and not elsewhere. Something weird: if I disable flow control on the PC, then I'm losing more - 9 to be exact. So maybe it's working normally at all and the GBA sets CTS to 1 after a (very short!) timeout. It's better than nothing, but still worrying since once my input buffer is full, I must wait for a thread to read bytes before accepting new ones. So I'd like the possibility to make the sender wait as long as necessary.
It might be interesting to know that during some wrongly run tests during which the GBA completely screwed up, I got the 'cat' command on the PC blocked, as if it was waiting for a signal from the GBA to continue sending. So I deduct that the PC can stop sending if requested to - all the problem lies in how to make the GBA ask the sender to shut up.
I'll continue investigating this tomorrow, it's probably on the good way.
Alex.
#22104 - Gnurou - Sun Jun 13, 2004 2:33 pm
Some more informations. It's both encouraging and weird.
I've solved the problem with the timer0 interrupt interfering by changing the entry/exit of interrupt code so just masking off/on REG_IE with the current interrupt. It seems to work fine.
Here is the test I ran, with only the serial interrupt enabled:
Every received character is directly printed on the screen. The operation is fast enough so no characters are lost as long as the screen doesn't scroll. Every 10 characters, a wait period is introduced. I've tried with 3 different wait periods: empty loop of 100000 iterations (about 0.5 sec), 1000000 iterations (about 5 seconds) and 5000000 iterations (about 25 (!) seconds). The interrupt code is below:
Code: |
u16 Siocnt;
u8 Data8;
/* Disable this interrupt */
REG_IE ^= IT_SERIAL;
/* The error flag of REG_SIOCNT is reset every time it is read,
so we must keep it into a variable to read it only once */
Siocnt = REG_SIOCNT;
/* Character received */
if (!(Siocnt & SIO_RECV_DATA))
{
/* Reading SIODATA8 might have some side effect - so we
read it carefully, only once */
Data8 = (char)REG_SIODATA8;
agb_writechar(Data8);
/* Every 10 characters, introduce a delay */
if (++lcpt == 10)
{
int i;
for (i = 0; i < 100000; i++);
lcpt = 0;
}
}
/* An error occured? */
if (Siocnt & SIO_ERROR)
{
/* Just display a '?' to inform us */
agb_writechar('?');
}
/* Reenable this interrupt */
REG_IE |= IT_SERIAL;
|
Here is the string I send to the serial port, the beginning of my last post:
Thanks for the informations Jasper. Unfortunately I'm still losing chars. I have tried the simplest possible example, a raw sending of characters without using interrupts.
Here is what I see on the GBA screen when the 0.5 seconds delay is introduced every 10 chars (characters in red indicate the idle time occured right after them):
Thanks for ?nformation?er. Unfort?y I'm stil?ng chars. ? tried the?est possib?mple, a ra?ing of cha?s without interrupts?
Every 11 characters, I lose the 6 next, excepted the first time where I only lose 5.
Now you wanna know the fun part? With the two other tests (5 and 25 seconds of delay) I receive exactly the same things. Of course it takes much longer, but I'm not losing more characters, and the PC actually waits with the GBA.
From all this I deduct that:
1) The GBA and PC are well using hardware flow control (If I disable it on the PC I only get the first 11 characters and the others are lost forever) since the GBA is able to order the PC to stop sending stuff.
2) I'm making a misuse somewhere, for sure. How come the amount of lost characters is constant while the delays vary? Why 6 characters constantly?
Finding an answer to these questions would probably mean solving the entire problem. Thoughts, pointers from GBA gurus are more welcome than ever. :)
Alex.
PS: Actually, this might be enough to sort things out. If I don't disable the serial interrupt at the beginning of its interrupt handler, I should catch all the characters. Putting them in a small buffer (10 elements would be large enough) prior to doing any other processing would probably do the trick, as I know the flood would stop after a few ones.
Will give it a try ASAP.
#22110 - Miked0801 - Sun Jun 13, 2004 3:42 pm
Just making sure you aren't doing any DMAs anywhere and that in your Crt0.s has your SIO interrupt at the most important level.
#22117 - JasperW - Sun Jun 13, 2004 4:56 pm
Well I'm pretty much stumped why you are still losing characters even though there is some kind of CTS signal. From my experience it should work as you'd expect it to, but it's been a while...
Tomorrow I'll be reunited with my UART cable and I will run some tests with hardware flow control and see what I can come up with as I'm very curious what is causing this.
Jasper
#22123 - Gnurou - Sun Jun 13, 2004 6:12 pm
Miked0801 wrote: |
Just making sure you aren't doing any DMAs anywhere |
I'm using DMA at init time - but not a single while receiving the characters. Can you tell me why they would cause problems?
Quote: |
and that in your Crt0.s has your SIO interrupt at the most important level. |
Sorry, I don't get what you mean here. I'm using Jeff's crt0.S 1.28 with MultipleInterrupts enabled. Since I'm only using the SIO interrupt for my test, I've tried with SingleInterrupt and it does exactly the same.
Are you referring to some kind of IRQ priorities? Could you elaborate on this please?
Thanks,
Alex.
#22126 - Gnurou - Sun Jun 13, 2004 6:34 pm
Some more information, maybe useful.
With the same program on the GBA, I've tried sending multiple single characters to the serial line using echo:
$ echo -n -e "A" > /dev/ttyS0
I've noticed that if I send some during the delay introduced every time 10 characters are received, these ones arrive safely. So the flow control works fine in case there is a slight delay between characters.
Also, I've tried with ports configured at 115200 bauds at both ends, it performs identically.
#22140 - Miked0801 - Sun Jun 13, 2004 9:03 pm
Add some error correction. All multiplayer code I've written for GBA has needed heavy error correction so this could be the case as well. Add a CRC cehck to each packet and go - of course since your errors are regular it more than likely is a bug with your system. Don't know what to tell you though!
#22180 - JasperW - Mon Jun 14, 2004 10:27 am
Alright I did some tests today and came up with something. You can find the source I used here.
When I first ran it on my Win2000 machine using Tera Term Pro I got the exact same results as you. I was losing 6 characters after the pause. That surprised me a little as I thought CTS/RTS was done correctly by the GBA.
Then I remember that when I first started experimenting with serial communications I started out as simple as possible (9600 baud, no FIFO). So what I did was turn off FIFO on the PC side (in the windoze device manager) and it worked! Now all the character come through even after the pause and even if I increase the wait.
So it's the PC that's not doing it's job properly and still sends out buffered characters even though the GBA signals not to send. My experience with Linux is pretty limited so I don't know how to disable FIFO on the COM port but it should be possible given that it's Linux :)
Jasper
#22184 - JasperW - Mon Jun 14, 2004 11:49 am
Found this on Google:
Quote: |
7.2. Older serial printers that drop characters
Jon Luckey points out that some older serial printers with ten-cent
serial interfaces and small buffers really mean stop when they say so
with flow control. He found that disabling the FIFO in his Linux
box's 16550 serial port with setserial corrected the problem of
dropped characters (you apparently just specify the uart type as an
8250 to do this).
|
So apparantly stop doesn't really mean stop in modern flow control. This probably the same what's happening to the GBA as there is only a one byte buffer.
Jasper
#22189 - Gnurou - Mon Jun 14, 2004 1:46 pm
Jasper,
Thanks a million. Now I have a precise idea of what's going on. I've just tested your program on a Windows machine with the port properly configured and it works fine. With my interrupt test I've been still losing one character - but it's probably bad code on my side. I'll run more tests once I get the port well configured on Linux, which doesn't seem as easy as you suggested, as says http://www.tldp.org/HOWTO/Serial-HOWTO-18.html
Quote: |
Note that the interrupt threshold of FIFO buffers (trigger level) may be set at less than 14. 1, 4 and 8 are other possible choices. As of late 2000 there was no way the Linux user could set these directly (setserial can't do it). While many PC's only have a 16550 with 16-byte buffers, better UARTS have even larger buffers.
|
As of today neither, it seems. :(
The trick you posted, suggesting to set the port to a 8250 UART (which doesn't have a FIFO), works poorly for me unfortunately. Guess what - I'm only receiving one character on 16! The FIFO doesn't seems to be deactivated, but this looks more like a kernel driver bug. I'll ask the linux serial driver guys about this.
So it was that. The FIFO buffer of the UART 16550A is 16 bytes by default. RTS/CTS flow control requires a software part that stops sending bytes as soon as RTS goes low. But all the bytes that are put in the FIFO are sent anyway by the UART. This is a normal behavior, as the device on the other end is supposed to have a 16 bits FIFO as well, which is not the case for the GBA. Disabling the FIFO is the solution, or you could activate the GBA FIFO and set the PC's FIFO size to 4.
I'll be away for the rest of the week, so I doubt I can look at this closely. But I'll come back once I have everything properly working on both sides. I think I'll also write a comprehensive documentation about all this, since to my knowledge this has never been explained so deeply in any documentation.
#22195 - JasperW - Mon Jun 14, 2004 3:10 pm
Glad we finally found what was the problem. Looks like you're going to be doing some kernel hacking over the weekend :-)
Your Java OS for the GBA has sparked my interest by the way. Does it have a homepage? I've tried implementing PPP/IP/TCP for the GBA but when I got to TCP my code became very complicated. I lost interest because of a bug that caused everything to crash if the system was under heavy load for a while. But when I think about it now it might be related to this.
When my exams are finished I might have another look at it because the PPP driver was functioning pretty well. Good luck with your project.
Jasper
#22198 - Gnurou - Mon Jun 14, 2004 3:49 pm
It really seems to be a bug in the 2.6 series of the serial driver. I've tried on a 2.4 kernel and, guess what, it works perfectly! Just sent a mail to report this to the serial driver guys.
I'm sending entire text files right now to my GBA and it is receiving everything perfectly, without a single character drop, and using interrupts. Jasper, I don't know how much I could thank you. I'd never have managed to sort this out without your help.
Cool thing is that, of course, it also works at 115200 bauds.
Some infos about our OS and TCP/IP stack: you can find its homepage (unfinished, unupdated and not so useful yet) at http://www.lifl.fr/RD2P/JITS . It's named Java In The Small and is a research project, but it performs quite well already. The idea is to start from a complete JRE that would be degraded according to the applications you want to use on your final OS (OS customization). Doing so, you can get very small footprints for very dedicated, hence very performant, OSes. Our real targets are devices like smartcards, but we are prototyping on the GBA because it has the same features (ARM processor, RAM/EWRAM/ROM, serial line), has good development environments and it's so damn cool to see the face the furnitures guys do when you order 10 GBAs with flash2advance kits to "do research work". :)
About the TCP/IP stack, it works over SLIP for the moment. PPP is on the way. We already have it performing (working would be a big word, for some things are missing) on the PC target, over the serial port. WIth the low-level stuff working on the GBA, it should work soon there as well. It's entirely written in Java, excepted for "getByte" and "sendByte" of course, and is designed with embedded devices in mind. Maybe you could get some ideas from it for your own design.
No release of JITS has been made yet, but we'll probably make the source available under an open-source licence soon. If you want me to tell you when it happens, or if you want to discuss more about this, I suggest you contact me by mail (see my profile here) - but I probably won't be able to reply before Sunday since I'm off to a conference.
By the way, we also plan to put a GBA online with an HTTP server, just for the fun. Watch out our page! ;)