No, the format of the conversation is generally command-then-answer.
I dont have any control over the serial format, its the 8N1 format as usual. The speed I can slow down (waits between bytes) on this device, but would not like to go that route as I am unlikely to be able to do this on other devices.
If I 5X rather than 4X, surely I get even less time in my already busy ISR to run 2 * 115200 UARTS?
Clearly so. But in detecting bit edges with certainty, 4X buys you very little over 3X, so you're better off with that odd number, instruction count wise.
That's what I mean by "more mileage" out of you instructions.
Peter I wanted to thank you for your generosity in writing the burst code. I realize that much of it was geared to instruction and I appreciate that. I am still digesting it in my processor and may have some questions for you at a later date. Basically, that was kick-butt instruction and I think many novice Sxers like myself would benefit from referencing that.
I also wanted to point out that there are many similarities in our approaches. The only time I write to the ports is during the ISR, and it is the same amount of steps during each transmitter burst. So I beleive that my foundation is deterministic. I use flags in my ISR to signal my "non-ISR" time to perform reconfiguration and state loading functions for the next ISR. I thought my ISR reconfigure scenario, it too being state dependent, was fairly clever but I guess it wasn't as cool as I thought it might be. The ISR count triggers reconfiguration of the ISR frequency ... What I was impressed with in your code was the use of faster processing speed and higher bit counters, which like I said earlier, I am still trying to understand.
Are there any other conceptual differences between the two approaches that I am missing?
Just to throw in my 2 cents, as I didn't see this potential problem mentioned anywhwere.· In the case where you are receiving at 57600 and sending at 115200, you have no problem.· Which makes me suspect that what is causing your error at 115200 in and 115200 out is a buffer overflow.
If the output baud rate is just slightly slower than the input baudrate, at some point you will get out of sync between your output and input, and your 32 byte fifo will start to fill up.· After a while you will fill the fifo and lose a character.
You might want to add code to test the fifo level and see if it has overflowed.· From the sound of it, this occurs about halfway through the picture.
This might seem counter intuitive, but it only takes a fraction of a bit of baud rate error to cause this problem, it is a result of the aggregate data rate in bytes per second being slower on output than on input.
If your output baud rate is slower by even 1 %, after 10 characters you will be one bit behind the input.· After 100 characters you will be one whole character behind. (assuming 10 bits per character) This is not a problem for short enough packet lengths because your fifo accumulates the excess incomming characters. Eventually the fifo overflows and you lose a character.
An alternative test would be to adjust your ISR to be just a little faster than 115200, and see if the problem goes away.· Anything up to 2-3% will generally not cause a problem if your receiving serial port on the PC end is not more than 2-3% off also.
You are absolutely correct PROVIDED the UART receiver waits for the whole stop bit before it is permitted to receive again. This could not typically be the case as then ALL continuous transmissions could never be received if the receive clock were fractionally slower than the transmit clock.
Although I have no definitive specifications, I suspect that all (most) UART receivers will (must) permit reception of a new start bit some time after the passing of only a portion (half?) of the stop bit.
This then will permit some small level of transmit and receive clock discreptancies as one would expect.
In other words, each start bit re-syncs the reception sample clock, hence small errors in clocks are not cumulative beyond single characters. Inside a single character, of course cumulative clock errors must be dealt with, and that is typically achieved by having sufficiently accurate clocks.
Therefore, I believe James' problem is not one of buffer overflow, at least not due to slight clock discreptancies. Possibly he could see a buffer overflow if any inadequate flow control was involved.....and we don't know this.
In looking at his posted code though one can see that due to unpredictable timing of events (transmission and reception), varying jump-routes in his transmitter's bit transitions and receiver's sampler are occurring at significantly consistent times in his ISR, therefore causing jitter in his clocks.....exactly what we don't want in a high speed situation. So my belief is that curing this effective clock variation will solve the problem, and that could be effected with using a shadow register transfer in the ISR.
This solution is not 100% guaranteed however, as the problem could also be just running out of instructions in the occasional long ISR, throwing the consecutive interrupts off, and hence a "blip" in the data validity.
My bet is that the deterministic UARTs I wrote for him will solve the problem, albeit there is not a lot of execution cycles left (but I believe enough) when running at full tilt. An actual test can best verify this as I did not have access to the actual harware and operational circumstances.
On the other hand, I could be all wet................in any case, do enjoy the VP UARTs I wrote.
Actually it doesn't matter if the receiver waits for the stop or not, even though it re-syncs to the start bit every character there will still be a problem if the input characters come in faster than they can be sent out.
Think about it this way, if the length of time between input characters from start bit to start bit is 100 microseconds, and because your output clock isn't quite as fast, it takes 101 microseconds to do the output character, eventually you will accumulate more input than you have been able to output. The fifo takes up the excess, but eventually runs out of room, because in aggregate you are sending bits slower than you are receiving them.
The thing about this kind of problem is that it is subtle. If you try to do a test by sending to your self, it will *always* work, because your bit rates are *exactly* the same. The problem will only show up if your output rate is slower than your input rate. In a gross example think about what would happen if you were receiving at 115200 baud and sending at 57600 baud. Even though you receive each input character correctly, very shortly you would run out of space in the fifo and characters would be lost.
I've been bitten by such situations before [noparse]:)[/noparse]
You are correct........... IF the basis of analysis is: "if it comes in faster than it goes out" then the buffer will fill up.
But that was exactly my point.
By "cheating" a little in receiving the stop bit, and buffering it a little early (that is before the reception of the stop bit has completely finished), then we are gaining a little time for the re-transmitter, and it can start sending before the reception is completely finished. Now it has ample time to get the received data in the buffer back out, and the buffer will empty instead of fill.
The UART I wrote and described does this, and that, I believe, is the salient point in all this.
We are assuming here of course that the discreptancies between the UART clocks is small.
Please show me where I'm wrong in this assessment........
I think you are missing the forest for the trees, or the characters for the bits, in this case. The issue is the total time it takes for an input character versus the total time for an output character. If the input baud rate is exactly 115200 baud, and we have 8 data, a start bit and a stop bit, the input character will take 10/115200 or 86.8055 micro seconds. If the output baud rate is 115000 baud (a .17 % difference) the output character will take 10/115000 or 86.9565 micro seconds, pretty close to 150 nanoseconds longer for output than input. That means that after 86.8055/.150 or 579 characters, the output will be one whole character behind the input.
Every 579 characters the fifo will accumulate another character so that after about 16 kbytes of data, a 32 byte fifo will fill up. At that point, after another 579 characters, the output will be 33 characters behind and the fifo overflows.
"Cheating" on receiving the stop bit would gain half a stop bit on the *first* character only. It still takes 10 bits worth of time to send the character. If the input bits are faster than the output bits, data will accumulate before it can be sent.
Another way to look at it is by characters per second. In the 115200 baud case it would be 115200/10 or 11520 characters per second, at 115000 baud it would be 11500 cps. So we are getting characters sent to us at 11,520 cps but we can only send at 11,500 cps. Every second worth of continuous data we will get 20 characters behind. Once the fifo fills up with the accumulated backlog, you lose characters.
Suppose you start sending the first character of a message out as soon as the last data bit is received, we will postulate that is halfway into the last data bit, because we are perfectly in sync with the input start bit. We have one and a half bits before the next character comes in. In a perfect world, with identical input and output baud rates, the first output character would just finish being sent at the halfway point of the second input character's last data bit, and the process would continue without problem.
If your output baud rate is just a little slower than the input baud rate, then that first output character doesn't get done until a little past the middle of the second input character's last data bit. Each subsequent output character will finish a little later than the previous one, until eventually you are behind by a whole character. At this point the fifo always will have one character in it, at all times. As time goes on, it gets further and further behind. This issue comes up if the input data rate is any amount faster than the output data rate. Before the first output character is done, even by a fraction of a bit, the second character is received and must go into the fifo because the transmitter is still busy outputting the last .01% (or some fraction) of the stop bit. This causes the output characters to gradually slip in time compared to the input characters.
Eventually you fill the fifo, even though you are transmitting as fast as you can, because the input data rate is ever so slightly faster than your output data rate.
Let's say the UART receive clock and the re-transmit clock are identical, and also happen to be the same as the "foreign" data stream we are receiving. Also let's say that the continuous incoming stream and the receiving UART bit edges are aligned, and the re-transmitter is waiting for a byte in the buffer.
Now, as you say, let the receiving UART transfer the first character being received to the buffer at the instant the sample of the 8th data bit is taken, say in the middle of the bit. This leaves us with the balance of that bit plus the stop bit before the second character's start bit will fall.
So the re-rtansmitter senses the first character in the buffer, and will start its re-transmission of that first character at the same instant. That means we are starting the re-transmission of character one's start bit before we have received the "trailing end" of the first incoming character.
Now, if the baud clock of the re-transmitter were just a bit slow, then it would finish sending the first character a little later than the middle of the 8th data bit of the second character in the input stream, but still well before the start edge of the third character. So this has the EFFECT of making the re-transmit appear faster than the receive stream, and hence there is no "bunching up", or clock creep in the system. There is 1.5 bits of "slop" time available for the re-transmitter to "gow" into for every received charcter.
This should work perfectly for re-transmit clocks that are up to 1.5 bits "slow" over one character under ideal cases. In reality one needs to expect a little less because clock edges are not normally ideally aligned, and one cannot be sure exactly when receive samples are taken in any bit.
If you still feel my analysis is wrong, (and I feel I'm right) then I will spend the time to set up three independent UARTs to prove (or disprove) my position.
I think the proof is the fact that Javalin did the test and the fifo overflows just when the error happens!
I see from what you wrote above, that you are postulating that the receive clock and the transmit clock are *exactly* the same as the incoming data clock.· Well, that isn't the case.· The foreign data is faster, by some fraction, than the clock the SX is using to control the transmit of each bit.
Internal to the SX the clock for receiving data and sending data is the same.· It has to be unless you went to great trouble to do otherwise.· But that isn't the same as being exactly the same clock rate that the PC used to send the characters the SX is receiving.· That clock is almost surely different in speed to the SX interrupt rate, which is our receive and transmit clock for the SX.
I think you are interpreting the received clock to be the SX ISR clock used to sync to the incoming data.· That isn't what I was saying.· What I'm saying is that the data we receive is clocked at 115200 but the data we send is clocked at 115000.· That mismatch will have no effect on receiving individual characters (which we do with the 115000 baud clock), but the aggregate data rates being different, will cause the fifo to fill.· There is no way around that!· The whole point of having a fifo is to accommodate mis-matched data rates. Yes each character will be synchronized by it's start bit, but it still takes less time to come in than it takes for us to send it out.
The received data rate, as transmitted from the PC is faster than the outgoing data rate from the SX.·· It doesn't matter how soon you transmit the characters relative to receiving input from the PC; you still take 10 bit times per character at whatever your transmit clock rate is.· If the outgoing bit time is longer than the PC's transmitted bit time (what we received), you will fill the fifo and lose characters when it is completely filled.
I think it would be good to try such a test setup, but make sure your SX's data clock is just a little slower than the input data source's clock.· Your fifo will fill up eventually, unless you have pauses between your incoming characters.
Another way to think about it that might make it clear would be to do the following:· Draw a one inch line on some 10 square per inch graph paper.· Call the 1 inch the character from the PC.· Continue for some number of characters, putting a tick mark to indicate the boundaries of the characters, each character being 10 squares long.· Below that line, starting 1.5 squares before the end of the first character, draw another line 10.5 squares long and repeat to simulate a slower outgoing bit rate than the incoming bit rate.· Very quickly the start of your output characters will move past the start of the input characters and at that point the fifo will fill by one, and never catch up. Unless there is a pause in the input data.
Yet another way to think about the fifo and mismatched input and output data rates would be to picture a can with a small hole in the bottom.· Take a second larger can with a bigger hole in the bottom.· Hold it over the first can, and fill it with water so that water comes out of the big hole and goes into the first can with the smaller hole.··The bottom can will start to fill up, until the top can runs out of water.· If the top can is much bigger than the bottom can and holds enough water, the·can with the smaller hole will overflow.
I will try to illustrate here with what I think is a fixed size font:
Each input character is 10 Is, each output character is 11 Os. The first output starts halfway through the stop bit of the first received character. I used a ! to indicate the border between characters.· The ^ is used to indicate when the input character has been completely received and ready to be transmitted.
IIIIIIIIII!IIIIIIIIII!IIIIIIIIII!IIIIIIIIII!IIIIIIIIII!IIIIIIIIII! ········ OOOOOOOOOOO!OOOOOOOOOOO!OOOOOOOOOOO!OOOOOOOOOOO!OOOOOOOOOOO! ·········^ first in·^ 2nd just ^ 3rd must ^· fourth must ·········· is·sent··· makes it·· wait for··· wait for third ·········immediately· without··· 2nd out···· to finish ·····················having···· to finish·· so goes into ·····················to wait····so goes···· fifo until ······························· into fifo·· transmitter ································ until······ done with 3rd ································ transmitter ································ done with ································ 2nd ·
Notice how quickly the output starts to slip behind the input! This is a gross mismatch, but exactly the same thing happens with a small mismatch. The output character takes longer to send than the input character did to be received, and so the output gets further and further behind.
Another analogy is an escalator, the speed of the hand rail never matches the speed of the stairs, so your hand either starts to go ahead of you or behind you.
The easy solution for Javalin is to make sure his SX is outputting bits faster than the PC is sending them.· In that case·the fifo·will never fill at all. It would at most·have 1 character in it at a time.
That would be like this where I use 11 Is to be input and 10 Os for output. Again the ! is the border between characters, and the ^ indicates when a character is fully received and available to be sent.
IIIIIIIIIII!IIIIIIIIIII!IIIIIIIIIII!IIIIIIIIIII!IIIIIIIIIII! ··········OOOOOOOOOO! OOOOOOOOOO! OOOOOOOOOO! OOOOOOOOOO! ········· ^·········· ^·········· ^···········^·
The output character takes less time to send it's 10 bits than the input character, so there is no problem keeping up. In this case the transmitter is always done before the next input character is done.
For 50Mhz clock at 3x 115,200 baud the ISR count would be 144.67 cycles.· If·we round up to 145 cycles the real ISR rate will be 114,942 which almost guarantees the fifo will fill.· If·we round down·to 144 cycles we get 115740 which should guarantee the fifo never fills, unless the PC transmit clock is faster still. In the first case·we are .2% too slow, in the second·we are .4% too high, but being too high·is the preferable way to err in this situation where·we have continuous input to process and then to output.·
Any pause in the input before the fifo fills up will also prevent the problem, but having a pause in input doesn't seem to be an option here.
Well, we are still on opposite sides, and I will do the tests.
Sure, I acknowledge that the buffer fills up, because that was proven it a test. But the REASON it fills up is now the issue.
Yes, I fully understand that the receive UART and re-transmit UART are working form the same clock, and hence there is no "slip" between them.
In postulating all clocks were the same, that was simply a reference point for determining timings.
My point was, said in another way, is that the software UARTs I designed dealt with received data before the end of the complete character, and THAT is what provides the "extra" time to permit the receiver/retransmitter pair's clock to be a little slower than the foreign clock.
I hope to get to this in the next several days, so please stand by......
I did something like the timing diagram you suggested, and found indeed you are correct!
In my example, I show two half-clocks per bit; SS is "start", 11 through 88 are the "data" bits 1 through 8, and PP is the "stop" bit.
Foreign data stream SS1122334455667788PPSS1122334455667788PPSS1122334455667788PPSS1122334455667788PPSS1122334455667788PP
Receiving UART SS112233445566778...SS112233445566778...SS112233445566778...SS112233445566778...
Retransmit UART SS1122334455667788PPSS1122334455667788PPSS1122334455667788PP
So, I can now see that regardless of when the re-transmitter starts sending, it always needs to transmit 10 bits per character continuously, and if the foreign stream is also continuous at a slightly faster clock rate, the re-transmitter eventually can't get rid of it in time, and data corruption results.
So what this implies is that one can never reliably "repeat" data at the same rate as it comes in without shortening up the repeated stop bit......
Sorry to have been such a pain about this, I was totally convinced of my position. Now I know better.....I learned several things today!
Peter, I guess we are both stubborn intelligent people! I did say earlier that it was a subtle problem. [noparse]:)[/noparse]
I just took the time to look over·your dual UART code, and I think it is very clever.· I see a couple of conceptual things I'm going to try to apply to some UART and buffer macros I wrote.·
The macros use only 2 bytes of ram plus the buffer space for each receiver and transmitter.· Because of that they are a little slow as there are a number of bit manipulations to keep pointers etc within those 2 bytes of overhead. I think I may be able to change them to use the state machine concept to reduce their execution time, fewer tests of counters etc.· For sure I can use the idea·of setting the receiver shift register to determine that 8 bits have been shifted by setting the carry on the last shift.· I think that is a brilliant case of killing two birds with one stone!
Using the shadow IO register in·a very short ISR with state machines in the main loop·to keep the bit timing in and out rock steady is a very powerful concept.· I have used a similar scheme for non timing critical things like blinking a led, but never for something like serial communications.
The only down side I see to using the state machine concept for timing critical items, is that all functions would have to be coded in such a way as to ensure the main loop gets back to the start before the next ISR happens.· If you had an operation that takes more time than available, you would have to make it a state machine and split it up into chunks that can be executed in less than the time until the next ISR.
Of course you could turn the concept upside down in the classic virtual peripheral way, still using the shadow register at the start of the ISR, but·have the ISR execute all the required time critical state machines·after the·I/O.· Then any time consuming tasks in the main loop get whatever is left·after the ISR completes until the next ISR.
James,
I'm pleased to see the that the fix was as simple as I thought.· Good luck with the rest of your project!
You know, I'm only stubborn when I think I'm right, and in this case I was in error. So, please excuse me.
Regarding state machines, sure, in some cases the state needs to conclude before the next ISR fires, and the writer needs to be aware that this may require some co-operation between routines.
When I have a bunch of independent yet timing critical things to do, I use my pre-emptive RTOS, and now I can write code at will without (much) regard for timing relationships. The're all independent, so a long task may be pe-empted by a more frequently running task.
Quite amazing how well it works. Only 99 bytes of code, but, unfortunately, quite a bit of RAM to save the thread states if all 8 threads are implemented.
Comments
No, the format of the conversation is generally command-then-answer.
I dont have any control over the serial format, its the 8N1 format as usual. The speed I can slow down (waits between bytes) on this device, but would not like to go that route as I am unlikely to be able to do this on other devices.
If I 5X rather than 4X, surely I get even less time in my already busy ISR to run 2 * 115200 UARTS?
Cheers,
James
Clearly so. But in detecting bit edges with certainty, 4X buys you very little over 3X, so you're better off with that odd number, instruction count wise.
That's what I mean by "more mileage" out of you instructions.
Cheers,
Peter (pjv)
I also wanted to point out that there are many similarities in our approaches. The only time I write to the ports is during the ISR, and it is the same amount of steps during each transmitter burst. So I beleive that my foundation is deterministic. I use flags in my ISR to signal my "non-ISR" time to perform reconfiguration and state loading functions for the next ISR. I thought my ISR reconfigure scenario, it too being state dependent, was fairly clever but I guess it wasn't as cool as I thought it might be. The ISR count triggers reconfiguration of the ISR frequency ... What I was impressed with in your code was the use of faster processing speed and higher bit counters, which like I said earlier, I am still trying to understand.
Are there any other conceptual differences between the two approaches that I am missing?
Thanks Peter
This thread has gone a bit off topic, so I'm posting a new DUAL UART RELAY thread to deal with your need.
Cheers,
Peter (pjv)
Just to throw in my 2 cents, as I didn't see this potential problem mentioned anywhwere.· In the case where you are receiving at 57600 and sending at 115200, you have no problem.· Which makes me suspect that what is causing your error at 115200 in and 115200 out is a buffer overflow.
If the output baud rate is just slightly slower than the input baudrate, at some point you will get out of sync between your output and input, and your 32 byte fifo will start to fill up.· After a while you will fill the fifo and lose a character.
You might want to add code to test the fifo level and see if it has overflowed.· From the sound of it, this occurs about halfway through the picture.
This might seem counter intuitive, but it only takes a fraction of a bit of baud rate error to cause this problem, it is a result of the aggregate data rate in bytes per second being slower on output than on input.
If your output baud rate is slower by even 1 %, after 10 characters you will be one bit behind the input.· After 100 characters you will be one whole character behind. (assuming 10 bits per character) This is not a problem for short enough packet lengths because your fifo accumulates the excess incomming characters. Eventually the fifo overflows and you lose a character.
An alternative test would be to adjust your ISR to be just a little faster than 115200, and see if the problem goes away.· Anything up to 2-3% will generally not cause a problem if your receiving serial port on the PC end is not more than 2-3% off also.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
MRC
Spot on!··I added a quick test and an LED to see if this was the case. Soon as the LED came on the image corruption occurred.
Cannot believe didn't catch this before! I was very puzzled that it worked for a while (aka it must be working) then showed the error.
Cheers for your help - I've been going arround in circles (fifo obviously) for ages....
PJV,
Thanks also for your help - I will be checking out your example code!
James
You are absolutely correct PROVIDED the UART receiver waits for the whole stop bit before it is permitted to receive again. This could not typically be the case as then ALL continuous transmissions could never be received if the receive clock were fractionally slower than the transmit clock.
Although I have no definitive specifications, I suspect that all (most) UART receivers will (must) permit reception of a new start bit some time after the passing of only a portion (half?) of the stop bit.
This then will permit some small level of transmit and receive clock discreptancies as one would expect.
In other words, each start bit re-syncs the reception sample clock, hence small errors in clocks are not cumulative beyond single characters. Inside a single character, of course cumulative clock errors must be dealt with, and that is typically achieved by having sufficiently accurate clocks.
Therefore, I believe James' problem is not one of buffer overflow, at least not due to slight clock discreptancies. Possibly he could see a buffer overflow if any inadequate flow control was involved.....and we don't know this.
In looking at his posted code though one can see that due to unpredictable timing of events (transmission and reception), varying jump-routes in his transmitter's bit transitions and receiver's sampler are occurring at significantly consistent times in his ISR, therefore causing jitter in his clocks.....exactly what we don't want in a high speed situation. So my belief is that curing this effective clock variation will solve the problem, and that could be effected with using a shadow register transfer in the ISR.
This solution is not 100% guaranteed however, as the problem could also be just running out of instructions in the occasional long ISR, throwing the consecutive interrupts off, and hence a "blip" in the data validity.
My bet is that the deterministic UARTs I wrote for him will solve the problem, albeit there is not a lot of execution cycles left (but I believe enough) when running at full tilt. An actual test can best verify this as I did not have access to the actual harware and operational circumstances.
On the other hand, I could be all wet................in any case, do enjoy the VP UARTs I wrote.
Cheers,
Peter (pjv)
Actually it doesn't matter if the receiver waits for the stop or not, even though it re-syncs to the start bit every character there will still be a problem if the input characters come in faster than they can be sent out.
Think about it this way, if the length of time between input characters from start bit to start bit is 100 microseconds, and because your output clock isn't quite as fast, it takes 101 microseconds to do the output character, eventually you will accumulate more input than you have been able to output. The fifo takes up the excess, but eventually runs out of room, because in aggregate you are sending bits slower than you are receiving them.
The thing about this kind of problem is that it is subtle. If you try to do a test by sending to your self, it will *always* work, because your bit rates are *exactly* the same. The problem will only show up if your output rate is slower than your input rate. In a gross example think about what would happen if you were receiving at 115200 baud and sending at 57600 baud. Even though you receive each input character correctly, very shortly you would run out of space in the fifo and characters would be lost.
I've been bitten by such situations before [noparse]:)[/noparse]
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
MRC
You are correct........... IF the basis of analysis is: "if it comes in faster than it goes out" then the buffer will fill up.
But that was exactly my point.
By "cheating" a little in receiving the stop bit, and buffering it a little early (that is before the reception of the stop bit has completely finished), then we are gaining a little time for the re-transmitter, and it can start sending before the reception is completely finished. Now it has ample time to get the received data in the buffer back out, and the buffer will empty instead of fill.
The UART I wrote and described does this, and that, I believe, is the salient point in all this.
We are assuming here of course that the discreptancies between the UART clocks is small.
Please show me where I'm wrong in this assessment........
Cheers,
Peter (pjv)
I think you are missing the forest for the trees, or the characters for the bits, in this case. The issue is the total time it takes for an input character versus the total time for an output character. If the input baud rate is exactly 115200 baud, and we have 8 data, a start bit and a stop bit, the input character will take 10/115200 or 86.8055 micro seconds. If the output baud rate is 115000 baud (a .17 % difference) the output character will take 10/115000 or 86.9565 micro seconds, pretty close to 150 nanoseconds longer for output than input. That means that after 86.8055/.150 or 579 characters, the output will be one whole character behind the input.
Every 579 characters the fifo will accumulate another character so that after about 16 kbytes of data, a 32 byte fifo will fill up. At that point, after another 579 characters, the output will be 33 characters behind and the fifo overflows.
"Cheating" on receiving the stop bit would gain half a stop bit on the *first* character only. It still takes 10 bits worth of time to send the character. If the input bits are faster than the output bits, data will accumulate before it can be sent.
Another way to look at it is by characters per second. In the 115200 baud case it would be 115200/10 or 11520 characters per second, at 115000 baud it would be 11500 cps. So we are getting characters sent to us at 11,520 cps but we can only send at 11,500 cps. Every second worth of continuous data we will get 20 characters behind. Once the fifo fills up with the accumulated backlog, you lose characters.
Suppose you start sending the first character of a message out as soon as the last data bit is received, we will postulate that is halfway into the last data bit, because we are perfectly in sync with the input start bit. We have one and a half bits before the next character comes in. In a perfect world, with identical input and output baud rates, the first output character would just finish being sent at the halfway point of the second input character's last data bit, and the process would continue without problem.
If your output baud rate is just a little slower than the input baud rate, then that first output character doesn't get done until a little past the middle of the second input character's last data bit. Each subsequent output character will finish a little later than the previous one, until eventually you are behind by a whole character. At this point the fifo always will have one character in it, at all times. As time goes on, it gets further and further behind. This issue comes up if the input data rate is any amount faster than the output data rate. Before the first output character is done, even by a fraction of a bit, the second character is received and must go into the fifo because the transmitter is still busy outputting the last .01% (or some fraction) of the stop bit. This causes the output characters to gradually slip in time compared to the input characters.
Eventually you fill the fifo, even though you are transmitting as fast as you can, because the input data rate is ever so slightly faster than your output data rate.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
MRC
Sorry, but I can't agree with your logic.
Let's say the UART receive clock and the re-transmit clock are identical, and also happen to be the same as the "foreign" data stream we are receiving. Also let's say that the continuous incoming stream and the receiving UART bit edges are aligned, and the re-transmitter is waiting for a byte in the buffer.
Now, as you say, let the receiving UART transfer the first character being received to the buffer at the instant the sample of the 8th data bit is taken, say in the middle of the bit. This leaves us with the balance of that bit plus the stop bit before the second character's start bit will fall.
So the re-rtansmitter senses the first character in the buffer, and will start its re-transmission of that first character at the same instant. That means we are starting the re-transmission of character one's start bit before we have received the "trailing end" of the first incoming character.
Now, if the baud clock of the re-transmitter were just a bit slow, then it would finish sending the first character a little later than the middle of the 8th data bit of the second character in the input stream, but still well before the start edge of the third character. So this has the EFFECT of making the re-transmit appear faster than the receive stream, and hence there is no "bunching up", or clock creep in the system. There is 1.5 bits of "slop" time available for the re-transmitter to "gow" into for every received charcter.
This should work perfectly for re-transmit clocks that are up to 1.5 bits "slow" over one character under ideal cases. In reality one needs to expect a little less because clock edges are not normally ideally aligned, and one cannot be sure exactly when receive samples are taken in any bit.
If you still feel my analysis is wrong, (and I feel I'm right) then I will spend the time to set up three independent UARTs to prove (or disprove) my position.
Cheers,
Peter (pjv)
I think the proof is the fact that Javalin did the test and the fifo overflows just when the error happens!
I see from what you wrote above, that you are postulating that the receive clock and the transmit clock are *exactly* the same as the incoming data clock.· Well, that isn't the case.· The foreign data is faster, by some fraction, than the clock the SX is using to control the transmit of each bit.
Internal to the SX the clock for receiving data and sending data is the same.· It has to be unless you went to great trouble to do otherwise.· But that isn't the same as being exactly the same clock rate that the PC used to send the characters the SX is receiving.· That clock is almost surely different in speed to the SX interrupt rate, which is our receive and transmit clock for the SX.
I think you are interpreting the received clock to be the SX ISR clock used to sync to the incoming data.· That isn't what I was saying.· What I'm saying is that the data we receive is clocked at 115200 but the data we send is clocked at 115000.· That mismatch will have no effect on receiving individual characters (which we do with the 115000 baud clock), but the aggregate data rates being different, will cause the fifo to fill.· There is no way around that!· The whole point of having a fifo is to accommodate mis-matched data rates. Yes each character will be synchronized by it's start bit, but it still takes less time to come in than it takes for us to send it out.
The received data rate, as transmitted from the PC is faster than the outgoing data rate from the SX.·· It doesn't matter how soon you transmit the characters relative to receiving input from the PC; you still take 10 bit times per character at whatever your transmit clock rate is.· If the outgoing bit time is longer than the PC's transmitted bit time (what we received), you will fill the fifo and lose characters when it is completely filled.
I think it would be good to try such a test setup, but make sure your SX's data clock is just a little slower than the input data source's clock.· Your fifo will fill up eventually, unless you have pauses between your incoming characters.
Another way to think about it that might make it clear would be to do the following:· Draw a one inch line on some 10 square per inch graph paper.· Call the 1 inch the character from the PC.· Continue for some number of characters, putting a tick mark to indicate the boundaries of the characters, each character being 10 squares long.· Below that line, starting 1.5 squares before the end of the first character, draw another line 10.5 squares long and repeat to simulate a slower outgoing bit rate than the incoming bit rate.· Very quickly the start of your output characters will move past the start of the input characters and at that point the fifo will fill by one, and never catch up. Unless there is a pause in the input data.
Yet another way to think about the fifo and mismatched input and output data rates would be to picture a can with a small hole in the bottom.· Take a second larger can with a bigger hole in the bottom.· Hold it over the first can, and fill it with water so that water comes out of the big hole and goes into the first can with the smaller hole.··The bottom can will start to fill up, until the top can runs out of water.· If the top can is much bigger than the bottom can and holds enough water, the·can with the smaller hole will overflow.
I will try to illustrate here with what I think is a fixed size font:
Each input character is 10 Is, each output character is 11 Os. The first output starts halfway through the stop bit of the first received character. I used a ! to indicate the border between characters.· The ^ is used to indicate when the input character has been completely received and ready to be transmitted.
IIIIIIIIII!IIIIIIIIII!IIIIIIIIII!IIIIIIIIII!IIIIIIIIII!IIIIIIIIII!
········ OOOOOOOOOOO!OOOOOOOOOOO!OOOOOOOOOOO!OOOOOOOOOOO!OOOOOOOOOOO!
·········^ first in·^ 2nd just ^ 3rd must ^· fourth must
·········· is·sent··· makes it·· wait for··· wait for third
·········immediately· without··· 2nd out···· to finish
················ ·····having···· to finish·· so goes into
················· ····to wait····so goes···· fifo until
····················· ·········· into fifo·· transmitter
································ until······ done with 3rd
································ transmitter
································ done with
································ 2nd
·
Notice how quickly the output starts to slip behind the input! This is a gross mismatch, but exactly the same thing happens with a small mismatch. The output character takes longer to send than the input character did to be received, and so the output gets further and further behind.
Another analogy is an escalator, the speed of the hand rail never matches the speed of the stairs, so your hand either starts to go ahead of you or behind you.
The easy solution for Javalin is to make sure his SX is outputting bits faster than the PC is sending them.· In that case·the fifo·will never fill at all. It would at most·have 1 character in it at a time.
That would be like this where I use 11 Is to be input and 10 Os for output. Again the ! is the border between characters, and the ^ indicates when a character is fully received and available to be sent.
IIIIIIIIIII!IIIIIIIIIII!IIIIIIIIIII!IIIIIIIIIII!IIIIIIIIIII!
··········OOOOOOOOOO! OOOOOOOOOO! OOOOOOOOOO! OOOOOOOOOO!
········· ^·········· ^·········· ^···········^·
The output character takes less time to send it's 10 bits than the input character, so there is no problem keeping up. In this case the transmitter is always done before the next input character is done.
For 50Mhz clock at 3x 115,200 baud the ISR count would be 144.67 cycles.· If·we round up to 145 cycles the real ISR rate will be 114,942 which almost guarantees the fifo will fill.· If·we round down·to 144 cycles we get 115740 which should guarantee the fifo never fills, unless the PC transmit clock is faster still. In the first case·we are .2% too slow, in the second·we are .4% too high, but being too high·is the preferable way to err in this situation where·we have continuous input to process and then to output.·
Any pause in the input before the fifo fills up will also prevent the problem, but having a pause in input doesn't seem to be an option here.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
MRC
Well, we are still on opposite sides, and I will do the tests.
Sure, I acknowledge that the buffer fills up, because that was proven it a test. But the REASON it fills up is now the issue.
Yes, I fully understand that the receive UART and re-transmit UART are working form the same clock, and hence there is no "slip" between them.
In postulating all clocks were the same, that was simply a reference point for determining timings.
My point was, said in another way, is that the software UARTs I designed dealt with received data before the end of the complete character, and THAT is what provides the "extra" time to permit the receiver/retransmitter pair's clock to be a little slower than the foreign clock.
I hope to get to this in the next several days, so please stand by......
Cheers,
Peter (pjv)
I did something like the timing diagram you suggested, and found indeed you are correct!
In my example, I show two half-clocks per bit; SS is "start", 11 through 88 are the "data" bits 1 through 8, and PP is the "stop" bit.
So, I can now see that regardless of when the re-transmitter starts sending, it always needs to transmit 10 bits per character continuously, and if the foreign stream is also continuous at a slightly faster clock rate, the re-transmitter eventually can't get rid of it in time, and data corruption results.
So what this implies is that one can never reliably "repeat" data at the same rate as it comes in without shortening up the repeated stop bit......
Sorry to have been such a pain about this, I was totally convinced of my position. Now I know better.....I learned several things today!
Cheers,
Peter (pjv)
Good explaination; I'm planning to use some of PJV's dual uart code to tighten-up my buffering code.
I will also now be experimenting with the ISR timing to overcome the re-transmiting issues.
Interesting......
James
I have just tried reducing my ISR RETIW value from 73 to 72 and it works!
Cannot believe the fix is *so simple* !!
Cheers for your help!
James
Peter, I guess we are both stubborn intelligent people! I did say earlier that it was a subtle problem. [noparse]:)[/noparse]
I just took the time to look over·your dual UART code, and I think it is very clever.· I see a couple of conceptual things I'm going to try to apply to some UART and buffer macros I wrote.·
The macros use only 2 bytes of ram plus the buffer space for each receiver and transmitter.· Because of that they are a little slow as there are a number of bit manipulations to keep pointers etc within those 2 bytes of overhead. I think I may be able to change them to use the state machine concept to reduce their execution time, fewer tests of counters etc.· For sure I can use the idea·of setting the receiver shift register to determine that 8 bits have been shifted by setting the carry on the last shift.· I think that is a brilliant case of killing two birds with one stone!
Using the shadow IO register in·a very short ISR with state machines in the main loop·to keep the bit timing in and out rock steady is a very powerful concept.· I have used a similar scheme for non timing critical things like blinking a led, but never for something like serial communications.
The only down side I see to using the state machine concept for timing critical items, is that all functions would have to be coded in such a way as to ensure the main loop gets back to the start before the next ISR happens.· If you had an operation that takes more time than available, you would have to make it a state machine and split it up into chunks that can be executed in less than the time until the next ISR.
Of course you could turn the concept upside down in the classic virtual peripheral way, still using the shadow register at the start of the ISR, but·have the ISR execute all the required time critical state machines·after the·I/O.· Then any time consuming tasks in the main loop get whatever is left·after the ISR completes until the next ISR.
James,
I'm pleased to see the that the fix was as simple as I thought.· Good luck with the rest of your project!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
MRC
You know, I'm only stubborn when I think I'm right, and in this case I was in error. So, please excuse me.
Regarding state machines, sure, in some cases the state needs to conclude before the next ISR fires, and the writer needs to be aware that this may require some co-operation between routines.
When I have a bunch of independent yet timing critical things to do, I use my pre-emptive RTOS, and now I can write code at will without (much) regard for timing relationships. The're all independent, so a long task may be pe-empted by a more frequently running task.
Quite amazing how well it works. Only 99 bytes of code, but, unfortunately, quite a bit of RAM to save the thread states if all 8 threads are implemented.
Cheers,
Peter (pjv)