As was foretold, we've added advertisements to the forums! If you have questions, or if you encounter any bugs, please visit this thread: https://forums.penny-arcade.com/discussion/240191/forum-advertisement-faq-and-reports-thread/
Options

RS-232 Communication on FPGA using VHDL

mcdermottmcdermott Registered User regular
edited May 2007 in Help / Advice Forum
Good times, let me tell you.

I've got the whole thing pretty much coded up. It simulates fine (using ModelSim). I put test signals in and out of it, it decodes them to parallel, good to go.

The only real worry I have is whether or not this thing is likely to actually synthesize and function when I put it onto the FPGA (Xilinx Virtex-4). My biggest concern is that my clock will be off no matter what I do; the FPGA runs on a 100MHz clock, and there's no way to divide that down to come out evenly to any common serial baudrate (I've gone with 9600).

Right now I'm clocking it using a basic counter, that oscillates every 5207 clock cycles, or basically about 6ns fast per cycle. Like I said, it simulates fine (I ran it through like 30 cycles...anything more starts to take forever) but obviously in the "real world" it's going to be running through a hell of a lot more cycles than that, and that 6ns will eventually add up.

I'm thinking this will be particularly problematic when talking about receiving...transmitting should still work out okay.

Anyway, the FPGA only offers TX, RX, and ground connections. None of the other 6 pins are functional. Has anybody had any luck getting bi-directional serial communications over RS-232 with such a setup?

Anyway, in case any more details would help I'm implementing this using a simple state machine; two, actually...one for receiving and one for sending. The receiving state machine sits idle for as long as RX is high, then on the falling edge of RX moves through 10 states: one to check the start bit, 8 to place the next 8 bits into an 8-bit register bit by bit, and one last that checks the stop bit, outputs the register, etc.

tl;dr: Is there any chance this will actually work? Yes, this means you have to go back and read it. Like I said, I think timing is the issue I'm most worried about here.

mcdermott on

Posts

  • Options
    mcdermottmcdermott Registered User regular
    edited May 2007
    BTW I can post code if it would help. However, I just tried it and of course it strips all the tab stops...making it damn near unreadable. That, and I haven't gone through and documented it yet (though it is pretty straightforward).

    Actually, here we go:

    rs232.txt - Code for the actual rs232 interface
    clock_div.txt - Code for the clock divider

    Like I said, it's not documented yet and needs some cleaning up.
    EDIT: Well fuck me...apparently how it looks in TextWrangler isn't how it comes out anywhere else...tab stop issues, methinks. So now it's even uglier after I tried to document it.

    mcdermott on
  • Options
    khainkhain Registered User regular
    edited May 2007
    Slight timing delays like this are usually expected since there sometimes isn't a way to perfectly divide a clock down, however I think you may have a problem in your receiving code. The way at least that I did RS-232 on a PIC chip was that the line is held at a 0 and it goes to a 1 you start reading 1/4 of a cycle after that. So that you read in the middle of where the signal you want is, this makes it so rise/fall times won't affect anything and slight timing delays won't do anything either.

    Example
    -                   |   |   |   |   |    |
    line at 0 ________---___---____---___
    
    
    
    
    

    My example sucks, but I think this may be your problem. I'm not familiar with FPGA code, but glancing at it your clock that tracks when you sample appears to be running all the time this would mean you don't know where in the signal your sampling from and with a slight time delay you could potentially sample incorrectly. If I'm wrong and your clock resets when you get the start bit then you should be fine ad the delay won't pass from cycle to cycle.

    khain on
  • Options
    mcdermottmcdermott Registered User regular
    edited May 2007
    khain wrote: »
    My example sucks, but I think this may be your problem. I'm not familiar with FPGA code, but glancing at it your clock that tracks when you sample appears to be running all the time this would mean you don't know where in the signal your sampling from and with a slight time delay you could potentially sample incorrectly. If I'm wrong and your clock resets when you get the start bit then you should be fine ad the delay won't pass from cycle to cycle.

    I do have the receiving portion pulling data mid-cycle...which cleared up my initial timing issues (which were severe). The states transition on the falling edge of the 9600Hz clock, and the data is read on the rising edge.

    The reason I don't reset the clock but rather have it running constantly is because I'm using the same clock for both the transmit and receive state machines, which operate entirely independently of each other. The only way I could have the receive clock reset itself (without fucking up the transmit machine) is if I ran two independent clocks. Which would probably be doable, since again the machines do essentially run independently. I was looking for something a little less ghetto, though.

    I don't know if I was entirely clear, but I've not actually implemented this in hardware yet. I've got the interface coded (two more separate vhdl blocks, one to handle some simple hard-coded bytes attached to buttons and another to display the last received byte by LEDs...and a top-level block to tie it all together), but didn't have time to actually synthesize and program the FPGA. For all I know this thing may actually work.

    However, everything I heard from other students who attempted this same general project made it sound as though getting it to receive successfully was a bitch, (if they even managed...most didn't) and this seemed relatively simple. While on the one hand I'd like to believe I'm just smarter than everybody else, I'm guessing I'm in for a nasty surprise tomorrow (luckily this isn't due until Friday...and hardware implementation is only 10% of the grade anyway).

    I was just hoping maybe somebody had attempted a similar project and might have some advice (or be able to predict the ways in which my design will implode tomorrow).


    Worst case scenario, I'll just implement a second clock, and reset it on every start bit on the receive line. Hopefully that would work if nothing else does.

    Also to anybody looking at that code, as long as you look at it with more than 80 columns it's readable (my comments just don't always line up).

    EDIT: Also, thanks for taking a look at it and for the input. I may actually go ahead and go straight for the two-clock approach rather than fuck around with this setup.

    mcdermott on
  • Options
    ThomamelasThomamelas Only one man can kill this many Russians. Bring his guitar to me! Registered User regular
    edited May 2007
    I'm not an engineer, but it almost sounds like RS-485 might be better then RS-232 for this. Two wires, and it is bidirectional. I know it's what we use for PTZ control for CCTV cameras, and in a pinch I've done direct wiring of 485 to 232 without a converter.

    Thomamelas on
  • Options
    khainkhain Registered User regular
    edited May 2007
    mcdermott wrote: »
    khain wrote: »
    My example sucks, but I think this may be your problem. I'm not familiar with FPGA code, but glancing at it your clock that tracks when you sample appears to be running all the time this would mean you don't know where in the signal your sampling from and with a slight time delay you could potentially sample incorrectly. If I'm wrong and your clock resets when you get the start bit then you should be fine ad the delay won't pass from cycle to cycle.

    I do have the receiving portion pulling data mid-cycle...which cleared up my initial timing issues (which were severe). The states transition on the falling edge of the 9600Hz clock, and the data is read on the rising edge.

    Are you sure you can guarantee this? As you said you check data on the rising edge, but what guarantee do you have the rising edge of your clock is in the middle of the data sent? You can't expect the data being sent and your clock to line up every time as its more likely that they won't. My prediction is that in the vast majority of tests your current program, excluding anything else, will work because the odds are good that you'll sample correctly and then in a few rare cases you'll end up with set of data being bad because of the 6ns delay and the clock not being in sync.

    -I'll try to draw something when I get home to make the point clearer since I may just be making a mistake in examining the code.

    -Using 2 clocks was at least normal in the class I took as well.

    khain on
  • Options
    ecco the dolphinecco the dolphin Registered User regular
    edited May 2007
    mcdermott wrote: »
    Right now I'm clocking it using a basic counter, that oscillates every 5207 clock cycles, or basically about 6ns fast per cycle. Like I said, it simulates fine (I ran it through like 30 cycles...anything more starts to take forever) but obviously in the "real world" it's going to be running through a hell of a lot more cycles than that, and that 6ns will eventually add up.

    Yes, you are correct. Your current implementation will have drift problems and is highly vulnerable to noise.

    Common implementations of RS232 receivers typically run the receiver clock *faster* than the baud rate and oversample each of the bits. A typical value I see is 12 times the baud rate. A decoder then figures out how many highs/lows were received, and might go, "> 6 highs => logic 1 was received, else logic 0 was received".

    This has two advantages that I can name off the top of my head:

    1.) More noise resistant.

    If you sample like you do now at 1 sample/bit, what happens if that sample happens to be on a glitch? You end up receiving the wrong bit. If you sample for 12 samples/bit and there's a glitch, at least 6 of the samples have to be on a glitch before you decode it into the wrong bit.

    The likelihood of at least 6 samples being on a glitch is extremely low compared to 1 sample on a glitch.

    2.) Better synchronisation.

    This is what you're looking for. At 1 sample/bit, you'd better hope that the incoming signal that is sent to you is somehow locked to your receiver clock. khain has already touched on this, so I won't spout off here.

    At 12 samples/bit, the incoming signal can start at pretty much any time. You will need to change the code so that it only starts decoding bits once a start bit is detected. You can, because you're not doing a practical implementation, say that a start bit is detected when a low is sampled after a long period of highs (idle).

    This means that you can effectively resynchronise your clock at the start of every byte, thus minimising drift problems with inaccurate clocks.
    Thomamelas wrote:
    I'm not an engineer, but it almost sounds like RS-485 might be better then RS-232 for this. Two wires, and it is bidirectional. I know it's what we use for PTZ control for CCTV cameras, and in a pinch I've done direct wiring of 485 to 232 without a converter.

    Sorry, 485 will suffer from the same problem. None of the wires in a 485 connection carry the clock signal, which is the problem that the OP is going through.

    ecco the dolphin on
    Penny Arcade Developers at PADev.net.
  • Options
    sirSolariussirSolarius Registered User regular
    edited May 2007
    Try it out and see. It's a little ghetto, but the RS232 spec has some provisions for drifting clocks.

    If you find that you're drifting a lot, make the frame size smaller so that the clocks resync more often.

    sirSolarius on
  • Options
    mcdermottmcdermott Registered User regular
    edited May 2007
    Yeah, it looks like oversampling would be necessary. Unfortunately, I'm not going to have time to implement that...so transmit-only it is. Yay for lowered standards!

    Thanks everybody, though, as you probably saved me as assload of time trying to troubleshoot something that wouldn't have worked reliably. Right now I'm discovering the joy of finding out that a large portion of my code that simulates beautifully doesn't synthesize at all. Oh well, it's a learning experience. And hardware implementation is only 10% of the grade anyway.

    I'm guessing we can go ahead and lock 'er up. Again, thanks for the advice.

    mcdermott on
  • Options
    ecco the dolphinecco the dolphin Registered User regular
    edited May 2007
    If you're still keen, the cheapest hack for you to get your receiver working would probably be to:

    1.) Normally keep your 9600 baud counter stopped.
    2.) Run a clock faster than the baud rate (again, 12?) - when you detect a low (start bit) after a set of highs (idle), wait for a set amount of time (e.g. half way through a 9600 period), and *then* start your 9600Hz receiver clock.

    This more or less puts the receiver clock smack dab in the center of the incoming signal, and allows it to resynchronise at the start bit for *every* byte. Still not noise immune, but you've probably got a clean channel anyway.

    Depending on how you've written your code (I admit, I didn't look at your VHDL), it might not be a big change because all you've done is changed the input clock to the receiver to start at a different time.

    ecco the dolphin on
    Penny Arcade Developers at PADev.net.
  • Options
    sirSolariussirSolarius Registered User regular
    edited May 2007
    mcdermott wrote: »
    Right now I'm discovering the joy of finding out that a large portion of my code that simulates beautifully doesn't synthesize at all.

    There's a word for that. It's called "electrical engineering."

    Imagine the largest cock in the world. Now double it. That's what ModelSim sucks.

    sirSolarius on
Sign In or Register to comment.