Portable C objects, Debugging a very fast RS232 library under propgcc
Fastrobot
Posts: 51
Hi, I port lots of code; so I've developed an expeimental way using macros to have some of the benefits of C++ objects when using a C compiler.
Although the propeller accepts C++, many embedded controllers do not; so I prefer to writing libraries in C, to make porting code less of a chore.
This kind of coding allows writing a simple C++ wrapper, with no real size penalty; so hardcore C++ users are not left out ( eg: can code for a universal C/C++ library).
Since gcc's preprocessor can be used with other compilers, it's also possible to preprocess gcc's varidac macros with GCC's CPP, but actually COMPILE to an object file with a different tool.
(That doesn't mean the principle behind the C pre-processor code is easily understood -- it's not... but the net effect is that the user interface is very easy to understand, and extremely portable.)
The intention is that I can do development of the code, and have gcc do all the proper type checking with it's typeof() operator; but once the code is syntactically correct, I can compile it to object code using ROSS-H's "Catlina" C compiler with minimal changes that are restricted only to the C pre-processor.
To test the idea, I wrote an RS232 driver object and library with several methods, eg: getc, puts, etc.
The program does compile under prop-gcc. (YAY!!)
In fact, up and until a few days ago -- it even worked.
Now, suddenly -- it stopped working and I don't know why. (Boo!)
I don't know what got changed or corrupted. I did upgrade my propeller gcc package to a newer one with less bugs, but I *think* the program still worked after the upgrade.
So, unless my memory is playing tricks on me -- I'm stumped.
There are a few benefits to this experimental serial/RS232 driver and technique for the propeller community as a whole; it is highly optimized, and hopefully is faster than the stock propeller drivers for the same level of reliability (I programmed it with metastable state timing jitter corrections); This particular driver is specially designed to buffer low priority data in a second buffer, which transfers blocks of data to the true RS232 buffer using a polling technique that only occasionally needs to be bothered when the main processor is not busy.
I did that to allow transmitting high priority data out of sequence/out of band (usually ASCII control codes) by minimizing data in the actual COG transmit fifo buffer. (eg: My target application is a 10 axis CNC controller, and real-time axis synchronization characters need to arrive as soon as possible or physical damage may result.)
The only general purpose downside is that my driver won't transmit ASCII code 0 (not fully binary stream compatible); As strings are teminated by 0 in C, this is generally not a problem. I have only developed the driver to transmit 7 bit ASCII code, and was intending on making a modified UTF-8 bit code and/or UTF-7 encoding, to allow the full benefits of highest transmission speed, and a binary data capable transmission protocol... (but only after my present project is complete.)
I'm wanting to use the parallax quickstart board's built in USB serial port to do debugging without interference, and so I bought a second USB serial port (AKA parallax's "propeller plug" product ) to actually test the driver's operation; and I talk to the prop plug using a linux modem program called minicom; (Windows used to have a program called "terminal" that basically does the same thing.)
I've attached the source code, photos of how the prop-plug is wired up on a Quickstart board and a linux build script to create the object files and load them into a Quickstart board using prog-gcc.
It does build and load, but no longer runs
If anyone can get it to work on your system, I'd appreciate knowing; as that would help me isolate why it stopped working on my system.
If it does work, I would also be interested in anyone's benchmarks as to how fast a baudrate my code will reliably sustain; and of course, it's open source -- enjoy!
Although the propeller accepts C++, many embedded controllers do not; so I prefer to writing libraries in C, to make porting code less of a chore.
This kind of coding allows writing a simple C++ wrapper, with no real size penalty; so hardcore C++ users are not left out ( eg: can code for a universal C/C++ library).
Since gcc's preprocessor can be used with other compilers, it's also possible to preprocess gcc's varidac macros with GCC's CPP, but actually COMPILE to an object file with a different tool.
(That doesn't mean the principle behind the C pre-processor code is easily understood -- it's not... but the net effect is that the user interface is very easy to understand, and extremely portable.)
The intention is that I can do development of the code, and have gcc do all the proper type checking with it's typeof() operator; but once the code is syntactically correct, I can compile it to object code using ROSS-H's "Catlina" C compiler with minimal changes that are restricted only to the C pre-processor.
To test the idea, I wrote an RS232 driver object and library with several methods, eg: getc, puts, etc.
The program does compile under prop-gcc. (YAY!!)
In fact, up and until a few days ago -- it even worked.
Now, suddenly -- it stopped working and I don't know why. (Boo!)
I don't know what got changed or corrupted. I did upgrade my propeller gcc package to a newer one with less bugs, but I *think* the program still worked after the upgrade.
So, unless my memory is playing tricks on me -- I'm stumped.
There are a few benefits to this experimental serial/RS232 driver and technique for the propeller community as a whole; it is highly optimized, and hopefully is faster than the stock propeller drivers for the same level of reliability (I programmed it with metastable state timing jitter corrections); This particular driver is specially designed to buffer low priority data in a second buffer, which transfers blocks of data to the true RS232 buffer using a polling technique that only occasionally needs to be bothered when the main processor is not busy.
I did that to allow transmitting high priority data out of sequence/out of band (usually ASCII control codes) by minimizing data in the actual COG transmit fifo buffer. (eg: My target application is a 10 axis CNC controller, and real-time axis synchronization characters need to arrive as soon as possible or physical damage may result.)
The only general purpose downside is that my driver won't transmit ASCII code 0 (not fully binary stream compatible); As strings are teminated by 0 in C, this is generally not a problem. I have only developed the driver to transmit 7 bit ASCII code, and was intending on making a modified UTF-8 bit code and/or UTF-7 encoding, to allow the full benefits of highest transmission speed, and a binary data capable transmission protocol... (but only after my present project is complete.)
I'm wanting to use the parallax quickstart board's built in USB serial port to do debugging without interference, and so I bought a second USB serial port (AKA parallax's "propeller plug" product ) to actually test the driver's operation; and I talk to the prop plug using a linux modem program called minicom; (Windows used to have a program called "terminal" that basically does the same thing.)
I've attached the source code, photos of how the prop-plug is wired up on a Quickstart board and a linux build script to create the object files and load them into a Quickstart board using prog-gcc.
It does build and load, but no longer runs
If anyone can get it to work on your system, I'd appreciate knowing; as that would help me isolate why it stopped working on my system.
If it does work, I would also be interested in anyone's benchmarks as to how fast a baudrate my code will reliably sustain; and of course, it's open source -- enjoy!
zip
87K
Comments
You probably know this, so for others, PST (Propeller Serial Terminal) and TeraTerm are two terminal programs for Windows. PropTool also has a simple one.
FWIW if $00 is needed to be transmitted by your driver there are a few tricks that might make this work. When passing the byte to the driver, often a byte is used for the character to be transmitted. If this byte is $00 then no character is available and this is why $00 cannot be sent/received. I use a long rather than a byte, and when a character is available I append/or $100 to it. This permits $00 to be transmitted and received. Another way is to use two characters. DLE+0 (ie $10 followed by $30) gives $00, and DLE+DLE (ie $10 followed by $10) gives $10. DLE=data link escape. This method was commonly used in synchronous communications many moons ago - I am too old to remember how many moons ago
In our commercial product, we use Catalina (and my own serial driver) to send data via a single serial port (there are only 2 pins available as the rest are used for 512KB of SRAM for the Catalina C Program, and microSD for file storage) to another propeller which then separates this stream into 3 serial ports plus some extra functions, and visa versa. Nothing needs to be fast in our implementation. FWIW I run the Catalina Propeller at 6.5MHz = 104MHz.
Here's a snapshot from my LA
It's supposed to be sending N7,2. Although, looking at your diagram I think it might be N7,1
Are you set up to receive N7,1?, or N8,1?
Lets see, the first message it's supposed to send is "\nStart\n";
So beginning with the first start bit (0), for N,7,1 it ought to be sending:
001010001 011001011 000101111 010000111 ....
In the first transmission, the second stop bit got corrupted, which I think is the clue that it's sending N7,1
Minicom only really sets the stop bits on the transmitter, so a mismatched stop bit on the receiver wouldn't cause my system to read bad data.
But it's good to know what it's actually doing.
I checked the source code, and it does look like it's set to send one stop bit.
In the file rs232.S, the stop bits are set on line 87; So, changing it to:
Should give you a second stop bit eg: N,7,2 (And should work with receivers in N,8,1 mode, too...)
That's not as fast a data transmission, because of one wasted bit time -- but it should be compatible with your LA settings.
As the baud rate increases, timing jitter caused by misalinged rdbyte and wrbytes, will eventually degrade performance.
It would be interesting to see how high a baud rate can be achieved before an edge is more than 1/3 of a bit timing off from where it ought to be.
It might be possible, ironically, by adding a few 'nops' to make the jitter statistically less, and the system more reliable.
Jitter shouldn't affect anything until the baudrate is up around 300 kilobaud or more @ 80MHz.
What are you running for a processor and clock?
I don't get any data at all, which basically means I either I have a hardware failure, or my compiler tools got corrupted somehow.
Hmmm....
My tools are linux, and are the build with August dates from your build server; David, did you use the same build for your Linux test or is there a new build?
I don't actually own a windows machine....I'm too poor. I spent all my money on a hex core overclocked real intel processor and massively fast memory, 4K 65" Panasonic monitor, and an R9-290 GPU. I didn't have enough money left after my wife divorced me to update windows.... But I do have a copy of windows98 second edition from years ago (before the divorce) when I used to work with windows. So those are new names to me, too.
I've seen tricks like DLE; I've seen it once or twice when I first started programing many years ago. The issue is that many people want to use DLE for their own purposes and so it's not standard and not automatically found in software drivers. It is a good idea, as are escape sequences for custom solutions in a pinch.
Passing data in a long would definitely solve the issue( EDIT: Epiphany: as would encoding the terminator as ($C0) or ($C1), eg: an illegal UTF-8 code. )
But passing in a long is a bad idea to the serial driver because it means the buffer data size would need to be a long, and eat memory for lots of nothing. (but speed is good.)
Your idea of appending $100 but modifed as appending $80 in 7 bit mode, is one I thought of before. That would allow the propeller to send ASCII 0, in N,7,2 mode.
The appended bit would hide under the "stop" bit during transmission and would be transparent. Adding the bit would be trivial in the interface code and would not degrade the assembly language driver for transmission. But... I hesitate to add that code as I am not sure I want to limit the transmitter to 7 bits in future versions.
(On the other hand, receiving a zero in the present code is a very bad idea, as the input buffer will hang until it wraps around; eg: a bug.)
I was thinking of using UTF-8 as it's found in most hardware and modern OS's; that's why I was thinking of solving it in that direction.
UTF-8 can encode 0 in multiple ways; and even though alternate encodings for zero are supposed to be illegal, a low level kernel driver like Linux is often not going to bother with the extra code needed to detect and report an error; rather it's likely to either pass it through unchanged, or covert it to a normal zero in the canonicalization process. I mean even wikipedia notes:
https://en.wikipedia.org/wiki/UTF-8
Which means that most OS's will accept this particular alternate encoding for the sake of strings automatically. The fact that the same technique will work for serial ports without ever modifying the OS driver on either end, is just a nice bonus. Any software which transmits to a processor which requires a '0' is going to be proprietary any way, and so I would think it not too painful to turn on modified UTF-8 encoding on most modern systems.
Edit: I might even use two different solutions for transmit an receive; For: In 7 bit mode, using $C0 to terminate the receive driver buffer would work, and since it's illegal UTF-8, it would still work even if I upgraded to 8 bit, so long as it had to be UTF-8 encoded. That would will allow 8 bit data to be transmitted as standard UTF-8, all legal like -- and get rid of the received 0 bug all at once.
Ahhh, welcome to the overclockers forum...! I'll have to get out a 6.5MHz crystal and see if my quickstart board will overclock...
Hopefully, the basic serial driver I've written is both smaller and faster and more reliable... so it's a win-win-win for most people.
Of course, the second (slow) buffer makes it larger as I've delivered it here, but that's for extra functionality. Compiled as a normal serial driver it should be smaller.
It is a simple two pin full duplex serial driver; so it works in most applications where hardware handshake is not required, exactly like the one you're describing.
That line fixed it. And switching my LA from 7N2 to 7N1 allows it to correctly decode with the original code.
I tested this on a DNA board, Ubuntu 15.10, no debug options, and build 2408.
When I use the latest version from my build server, no bits clock out. Something broken with the build, or the build server. Not sure which
Well at least we know where they problem is, even though we don't yet know why.
That's part of the ever lasting battle....
Well, I haven't checked all the data to see that the main program/interface library is properly calculating and downloading the initialization constants,
But I do know that when using GDB, I can't get control back after a continue with control C.
I have to kill gdbstub from the unix command line to get GDB to come back.
That suggests the linker downloaded the wrong program, or else the .pasm/gas assembler has assembled something wrong and memory is corrupted.
EDIT: maybe not, if I kill the monitor, and restart GDB -- I get control of the program back.
Oh, I can see something ugly; when I get inside M(com1,puts,...) and print *self,
GDB says:
So, the transmit pointer and receive pointer are both Null. That's bad.
They were supposed to be set on lines 42 and 42 of rs232lib.c, although I cheated when I did it and am counting on a void* converted to long being what the propeller requires; It did work once before....
The struct looks like:
and I initialize it with:
but even changing it to a more correct form:
changes nothing.
Oh, I see!
If I print the struct out just before it calls cognew, everything looks perfectly OK.
after it returns, GDB says self was optimized out.
But I can still say:
So, that pretty much means the assembly language routine or something in cognew is destroying the values.
Was GAS edited/worked on? I think it very likely it's assembling the code differently.
So, I just copied it to another variable to get the address, and then inspected that with GDB....
then I did a memory dump of the fist 15 instructions. Those are the assembly language responsible for erasing PART of the structure, and I assume the most likely culprit for this bug is that it is erasing the whole structure for some reason. The assembly code is never supposed to modify the pTx or pRx members normally, so I would be very surprised if the error were anywhere else in the routine.
Note, I removed all the symbol names as they are bogus; but this is what the dump looks like.
I don't have a working copy of the program to be able to dump the code and compare, but... my comments are <~~~ and in []
Whatever is wrong, I don't see it -- but maybe I'm too tired.
Compare against the assembly language code:
EDIT:
Whoa!
I found a mistake in my code, and changed it -- and now I can get it to work. But it was not a mistake that I would have expected to have caused the problem.
I originally wrote this for GAS, assuming byte alignment -- so I defined a macro WA to handle word alignment for gas vs. pasm and make the register differences be switchable between 4 bytes or one cog register; depending on whether compiled on gas/gcc or Catalina.
Leaving it four, spaced the register locations out by 4 words. Since they are all after the end of the program, I wouldn't have expected that to cause the effect observed.... but just to waste memory. But now I realize that the pointer to the buffer was initialized wrong, and it was clearling bytes at a random memory location.... blind luck....
When I added the .pasm directive to fix another bug, I ought to have changed WA to 1. But I forgot.
I did test this program on a slightly older compiler, and it ran even with adding .pasm on the older compiler.
That definitely means there is another bug in the older compiler....
Did you get a warning saying that .pasm was not accepted when you compiled it on the earlier build?
I'm not sure where the up to date source is supposed to be, but since I was planning to explore GCC's optimizations/peep-holes, I decided to make an attempt at a source code compile.
I went to: https://github.com/parallaxinc/propgcc
I did a git clone: (Thankfully I have lots of removable hard drive space on /mnt... as compiling this seriously eats gigabytes...)
git clone https://github.com/parallaxinc/propgcc.git gitPropGCC/
cd gitPropGCC
make -PREFIX=/mnt/propgcc_11_2015 -j11
Although there were a few warnings during the make process .... it built all the tools on slackware 14.1, x86_64 happily. (I love compiling make -j11...but it's hard to see any non-critical error messages scrolling by ....)
So, then to satisfy my curiostiy, I did:
propeller-elf-gcc -v
I get:
That extra string: -4-g589af82 looks to be a totally different nomenclature than 2408, but does it mean anything to you? What build number is the present git hub source code for Nov-8-2015 supposed to be?
Notably, the build also created gdb. I'm quite happy about that.
I just copied my gdbinit from the rs232 test program,
cp gdbinit /mnt/propgcc_11_2015/lib/gdb/gdbinit
And I was able to run the program from gdb as usual.
I was curious, though, because the git hub clone's "commit" comments indicate that no changes have happened in at least four months. But we were discussing issues with it not compiling with the version number just a month ago, and ncurses was also a subject that was just mentioned in another thread... but github claims that ncurses was added to the repository 8 months ago. Is it my imagination, or is there something wildly inaccurate about github's date record system?
It also turns out that the repositories are not identical. I'll have to poke at it after work some more, but whatever configuration I had working before isn't compatible, because I now get "permission denied" errors about creating the /opt/parallax directory.
Build Number
The first four is weird... I have no idea what that is. The second piece, g589af82, is the git hash. I don't know why there is a "g" at the front, but the rest corresponds to this commit.