uart2

RS232 UART (VHDL)

Description

I designed the UART core to allow me send and receive data from a Spartan 6 LX9 Microboard which has an on board Silicon Labs Cp2102 USB-UART Bridge.

The UART Core is a simple RS232 communications controller which can be used to provide a quick and easy means of communicating with your FPGA board. The baud rate used by the controller is easily set using the generic map, although I have only tested it so far with a rate of 115200 baud using a 100MHz system clock. The communications scheme used is 8 data bits, no parity and 1 stop bit (8N1), so make sure your terminal software is using these settings if you have any problems.

Internally, the UART provides a simple interface incorporating a data strobe and acknowledge for sending and receiving data. To send data from the FPGA, the data must be presented to the DATA_STREAM_IN port and the DATA_STREAM_IN_STB (Strobe) asserted; when the DATA_STREAM_IN_ACK (Acknowledge) output is asserted by the UART the data has been sent. The strobe should be deasserted 1 clock cycle after the acknowledge is seen to avoid an additional byte of data getting sent.

Handshaking Scheme

Specifications

  • RS232 8 data bits, 1 stop bit, no parity data stream format
  • Customisable baud rate
  • Simple internal interface and handshaking scheme

Download

You can grab the source code for the UART through my GitHub repository here.

Posted in Projects, VHDL and tagged , , .

63 Comments

  1. Hi,
    I am doing a project involving rs232 communication.
    I analyzed your code and tried to your apply baudrate generator to my own code. But I cannot understand function what log2 does. Could you explain what it does?
    thanks.

    • Hey Musa,

      Simply put I’m using the log2 function to work out how wide I need to make the receive and transmit divider counters.

      constant c_tx_divider_val : integer := CLOCK_FREQUENCY / BAUD_RATE;

      The width of the counter that counts up to c_tx_divider_val is log2(c_tx_divider_val) + 1.

      You could just write in the constant widths to keep your code clean if you like and remove the log2 function as its not used for anything else.

      If you would like any help feel free to ask.

      Pete

    • Further to my previous reply:

      I’ve changed the baud_counter and oversample_baud_counters to integers and removed the need for the messy width calculations, I think I was originally using the unsigned type to take advantage of the clean wrap-around overflow to 0, but this isn’t necessary. Its been a while since I looked at this code, so thanks for drawing my attention to it.

      The updated file is on github and a quick test on my hardware shows that it’s working fine. I hope you found this helpful.

  2. Looking at your code…

    if baud_counter = c_tx_divider_val then

    This means that you need to build a large comparator to match the results.

    It would be far better to add one more bit to the counter, then load the value, decrement the counter and catch the high bit “underflow” when the counter raps round.

    That way you are only having to compare a single bit rather than the full length of the counter.

    • Hi Shedo,

      I’ve updated the link in the post to point to a dedicated repository for the UART with a simple loop-back example, hope this helps.

      Pete

      • Hi
        is the code will work with 9600 baud rate on SPARTAN 6 FPGA, ATLYS Board? Can we send data to FPGA from PC using a MATLAB program, and also receive back in MATLAB

        ADI

          • Hi Pete
            I checked the code with my Spartan 6 Atlys board, with PC interface, using MATLAB Program.
            Following is my MATLAB code, that is unable to receive the data, but can send it, (verified by recording the receive and transmission, using record(on). The record file is also given below, How we can diagnose, the problem in reception? Pl. guide me

            ADI

          • I’m not sure why you’re having problems ADI, it’s working for me and we both have the same board. Try using some software like Terraterm to test the loopback, it could be an error in your script. The code on Github has some additional changes to flash a heartbeat LED and also flash LEDs LD1 and LD0 upon receiving and transmitting a byte, maybe try this new version and confirm that the LEDs behave as described.

            I removed your posts with the Matlab script output to keep this thread tidy!

  3. Hi Pete
    Your code is now working properly, with the Atlys board, but getting message in MATLAB
    “Warning: The specified amount of data was not returned within the Timeout period.”

    Thankx
    ADI

  4. what software did you use to make this module. i’m just intrested because the rtl and simulation look good.

  5. Hi..i am using you code in Arria II GX FPGA and tested your simple loopback block. But unfortunately it is not working on my debug terminal. My results are as below:
    for example if i type ASCII Letter ‘a’ on debug terminal i get some ASCII symbol instead of getting ‘a’ back in the loopback test.
    Note that i didnot do any change to your code. Using the code as it is..clock is 80MHz instead of 100MHz…Can you please let me know what could be the problem?

  6. Hi,
    thanks Pete.
    I am doing a project involving rs232 communication (115200 bauds with clock at 80000000 hz) but i have a problem. My serial link works in my CYCLONE III after around 25 secondes instead of immediately.
    Do you have a idea of my problem?
    thanks!!!

    • Hi Seb,

      I’m not sure why you would get such a delay, I noticed something similar in the past when I used Terraterm, so if you’re using this you could try something else like Python with PySerial and see if the problem goes away. Let me know if you find a solution to your problem because it could be something that needs fixing in the VHDL.

  7. Hi Pete!

    I am a beginner trying to use your UART module to communicate with a RN42. Unfortunately I can’t compile uart.vhd. It either complains that the generic values has no actual values. When I try to assign a value, it complains that line 80 is negative. Help!

    Steph

    • Hi Steph,

      It sounds like the values you are assigning to the clock_frequency and baud_rate aren’t valid. If you check out loopback.vhd you can see an example of using the UART in a design. In that example the baud rate is set to 115200 bits/sec and the clock frequency is set to 100000000Hz (100MHz). You can change those values to match your hardware, just make sure your chosen baud rate is achievable for the clock frequency you’re using.

  8. Hi..
    Pete
    I have checked the code with Spartan 6 Lx 9 Board.It is not working properly.Could you please send me the detalied procedure how to use this code and how to check the output

    • Hi Venky,

      The code should work fine with the LX9, its the same board that I tested it on. I sent you a bitfile that implements loopback using 115200 baud, you should be able to deploy this on your LX9 and talk to it using some COM port software, any data you send with be echoed back.

  9. Hi Pete,

    I want to Implement RS422 communications using Spartan 6 LX9 Board.On one side i have rs 422 transceiver,from the transceiver the rs 422 signals are given to PMOD’S of the board.Could you please send me the code for implementing RS422 from PMOD’S using AXI LITE 4

  10. Hi Pete,
    Instead of looping back the uart data i want it to be sent to the pmod pin of the board.I sit possible.If possible send me the code for that please

  11. I want to do uart communication between two PC’S.From one pc i will send the tx data to pmod of thge board,i want the data received on the pmod pin to be displayed on serial terminal.similarly whatever the data enterd in the serial terminal has to be placed on the pmod pin.Please send me the code

  12. Hi Pete,
    I have tried in a lot of ways but in vain.Could you please help me in this regard…I am unable to access the data from Pmod pin of the LX9 Board

  13. What is the max baud rate this design can go.Can i use this to generate baudrate of 12M with clock of 200Mhz

    • That is quite a high baud rate, the FT2232D for example goes up to 3M baud. It might be possible if you have hardware to support it and the design meets timing. The 16x oversampled receiver clock would need to be 192MHz, but would be realised as 200MHz (~4%), which I think is within tolerance.

  14. Yes i can receive it using FTDI 2232H wich supports upto 10Mbps data rates. But at higher Data rates my baud rate is falling in this design. i need to get 10mbps but im seeing around 9mbps. how to rectify that in this design.

    • I guess the code to generate the baud rate is failing for your particular input frequency and baud rate combination. If you are able to, try it with a 160M input clock. For 10m baud an input clock of 160M should give you sensible results with TX at 10M and RX at 160M. If there are any problems, try the above with this version of uart.vhd, which is the original code without any changes to the baud generator.

      • actually i’m doing with that code only .by using integers.Still same, baud rate falling . i will try ur idea 160M clock for 10M baud and 192M for 12M baud rate

        • Buad rate is falling because your baud counter is counting one count more check line 102 and modifie it. if baud_counter = c_tx_divider_val – 1 then i will get exact value thank you for the code and help…

          • Thanks for the fix. This would only apply to the older version of the UART, did you manage to get the most recent version of the UART working with your chosen baud rates and updated clocks?

  15. In the transmit portion of the code, baud_tick is created by these lines of code:
    —————————————————————————————————————
    TX_CLOCK_DIVIDER : process (CLOCK, RESET)
    begin
    if RESET = ‘1’ then
    baud_counter ‘1’);
    elsif rising_edge (CLOCK) then
    if oversample_baud_tick = ‘1’ then — Use as Clock enable
    if baud_counter = 0 then — If 16 oversamples have occured
    baud_counter ‘1’); — Set the counter to 16 again
    else
    baud_counter <= baud_counter – 1; — Decrement until 16 ticks have occured
    end if;
    end if;
    end if;
    end process TX_CLOCK_DIVIDER;
    — And thats the baud tick, which is of course only one clock long
    — So both counters should be Zero
    TX_TICK: baud_tick <= '0' when RESET = '1' else
    '1' when oversample_baud_tick = '1' and baud_counter = 0 else
    '0';
    ———————————————————————————————————————

    I'm not sure I understand where 'oversample_baud_tick' is set during the Tx clock generation. Thanks for the help!

    • Hi Steve,

      To test the implementation you should simply connect the UART bridge port on the VC707 to your PC via a USB cable and set up your COM port software to use 115200baud 8N1. The loopback firmware just replies with whatever you send it, so if you’re typing characters into your COM port software they will be echoed back.

      If this doesn’t seem to be working ensure you correctly constrained the USB_RS232_TXD and USB_RS232_RXD pins to AU36 and AU33 respectively. The system clock on the VC707 is 200MHz, so if this is being directly connected to the CLOCK_Y3 port of the UART you will need to edit loopback.vhd to change the clock frequency constant from 100MHz to 200Mhz. Alternatively you can use an MMCM to generate the required 100MHz.

      Finally: RESET is active high, so if you have mapped it to any external IO ensure that the inactive state of the IO will be a 0.

      Pete

  16. This work looked interesting to me. I was trying to understand how it works. I see the loopback code. I assumed if I edit the line 112: uart_data_in <= uart_data_out; in the loopback.vhd file to some other byte, I could use to send back some other data rather than the same data. But it didn't worked. Can you explain why it doesn't work this way?

  17. Hi Pete,
    Thanks for the UART source code. This really helps my project. But I get some problem using your code. I successfully test the UART with loopback test, but when I send bytes in packet which contains 30 kB data, I get some missing characters when the packet retransmitted to PC. I only get around 25 kB data. Can you help me to fix this problem?

    Regards,
    Garry

  18. I think the code has some bug. I tried the following byte stream and it looped back wrong stream. Test stream: 00 01 00 01 04 05 04 05 00 01 00 01 00 01 00 01 00 01 00 01 00 01 04 05 04 05 00 01 00 01 04 05 00 01 01 04 05 00 01 00 01 00 01 00 01 05 00 01

    Some bytes goes missing!!

  19. Hi Pete, I know its been a long time since the last post/question but I would appreciate your thoughts on this – I’ve written loopback (I believe) that between PuTTy and a SP605 re transmits back whatever is typed into the console, but every 3rd character disappears! E.g. if you transmit out the string 1234567890 I receive 1224557880 – I’ve been scrutinizing your code but cant see where this occurs! Any ideas?

    • Hi David

      Its been a while since I’ve looked at that code, but you may want to try the AtlysSpartan6 branch on github; it contains the original design that I uploaded without the additions from other users. It looks like a few other users are having issues with missing data, so there may be something wrong with the design on the master branch.

      edit: I’ve had a look at the design and found a few issues, these should be resolved in the latest master.

  20. Thanks for posting this code… I was able to very quickly and easily port it over to the miniSpartan3 dev board (I posted the project over on their forums: https://www.scarabhardware.com/forums/topic/ftdi-uart-communication-in-vhdl/ ). I believe there are still two minor bugs in the rate generation though.

    Lines 56-59:
    constant c_tx_div_width : integer := integer(ceil(log2(real(c_tx_div))));
    constant c_rx_div_width : integer := integer(ceil(log2(real(c_rx_div))));

    When c_tx_div or c_rx_div are a power of 2, this creates a counter width that is 1 bit too narrow, causing the equality checks (line 116: “if rx_baud_counter = c_rx_div then” and line 250: “if tx_baud_counter = c_tx_div then”) to be optimized out, ruining everything. For example, if c_rx_div equals 8, it’ll specify a width of 3 bits, which of course can never equal 8.

    I changed those lines to remove the ceil (causing a floor), and just always add 1:
    constant c_tx_div_width : integer := (integer(log2(real(c_tx_div)))+1);
    constant c_rx_div_width : integer := (integer(log2(real(c_rx_div)))+1);

    With this, I’m able to successfully run at 230400 w/ a 32 MHz clock. It seems to fail at 460800 and 921600, but I’m guessing they’re just not a good match for the clock, which could be fixed with a DCM. And I didn’t try any non-standard rates… just what was available in HyperTerminal.

    DogP

    • No problem and thanks for the fix! I guess I should have included more baud rates in the unit tests. I’ll apply your changes to the repository after I’ve run some tests.

  21. Hi Pete,
    I am a beginner and trying to make a UART module to communicate with pc. Unfortunately I can’t compile .when i try to simulate it it said that ,line 11: Generic has not been given a value. It either complains that the generic values has no actual values. When I try to assign a value , it said that
    =========================================================================
    WARNING:Xst:616 – Invalid property “baud 9600”: Did not attach to uart_inst.
    WARNING:Xst:616 – Invalid property “clock_frequency 100000000”: Did not attach to uart_inst.
    WARNING:Xst:616 – Invalid property “fifo_depth 1024”: Did not attach to receive_buffer.

    can you please attach video with assigning baud rate and and clock frequency , and simulation steps ..?

  22. Hello.

    I think there is a fault in your code. You give two statements. On your website you write (1):
    “… when the DATA_STREAM_IN_ACK (Acknowledge) output is asserted by the UART the data has been sent.”

    In your uart.vhd in line number 14 to 16 you write (2):
    — data_stream_in_ack
    — Output acknowledge to indicate the UART has begun sending the byte
    — provided on the data_stream_in port.

    I checked your code, the second statement(2) is true. Further it makes no sense to notify other components that sending the byte has begun. You want to know when it is finish so you can start the next write process. I could not comprehend your code. Maybe you had a different intention in coding it like this. Maybe you can tell me.

  23. Hey,

    I can not understand why did you multiply the baud rate by 16, can you please explain that?
    constant c_rx_div : integer := clock_frequency / (baud * 16);

    • Hi Chris, the receiver side uses a clock frequency of 16x the baud rate so that it is able to detect the beginning of the start bit and re-align its sampling point with the incoming signal. This is done because you can’t guarantee that the two communicating devices share the same clock and may be out of phase or have different frequencies, so the receiver side needs to recover the transmitter clock and re-align itself with the incoming signal.

  24. I can not get chiptools to run on my pc, i have the latest vivado scuite installed, but it says it couldnt find anything

    [WARNING] Quartus synthesis tool could not be found. Update .chiptoolsconfig or
    your PATH variable
    [WARNING] Vivado synthesis tool could not be found. Update .chiptoolsconfig or y
    our PATH variable
    [WARNING] Ise synthesis tool could not be found. Update .chiptoolsconfig or your
    PATH variable
    [WARNING] Iverilog simulation tool could not be found. Update .chiptoolsconfig o
    r your PATH variable
    [WARNING] Ghdl simulation tool could not be found. Update .chiptoolsconfig or yo
    ur PATH variable
    [WARNING] Isim simulation tool could not be found. Update .chiptoolsconfig or yo
    ur PATH variable
    [WARNING] Vivado simulation tool could not be found. Update .chiptoolsconfig or
    your PATH variable
    [WARNING] Modelsim simulation tool could not be found. Update .chiptoolsconfig o
    r your PATH variable

    how can i test the uart code without the chiptool, or how et it to run?

    • Hi Trax,

      Try following the instructions here: configuring Chiptools

      Chiptools will not find Vivado unless it has an entry on your system PATH or it is added to the .chiptoolsconfig file. If you decide to edit the .chiptoolsconfig file instead of your PATH you can find it in your home directory (c:\Users\ on Windows) and you will need to add the following lines:


      [simulation executables]
      vivado = "path to Vivado"

      [synthesis executables]
      vivado = "path to Vivado"

      You can use the code without Chiptools of course, but you won’t be able to run the unit tests.

      Pete

  25. Hi,

    I had a small confusion. Why in test bench, you did this calculation
    remote_clock_int <= not remote_clock_int after (
    (1.0 / (2.0 * real(remote_clock_hz))) * real(1e9)
    ) * 1 ns

    and how you calculated delay_ns as 0.75?

Leave a Reply to Hardcorefs Cancel reply

Your email address will not be published. Required fields are marked *