From Hamsterworks Wiki!
Please make sure that you have completed the earlier modules of FPGA course.
Having problems with your code? Do I need to change something? Want a reference solution to the project? Email me at "email@example.com", after removing the "nospam-" bit. I'll try to get back to you in a day or so.
Aims of module
- Use a shift register
- Send data to a PC over RS232
What is RS232?
RS232 is a very old standard originally used to interface digital systems with analogue phone lines and data circuits. It enables relatively low speed communication between devices, and is relatively simple to implement.
If hardware handshaking is not used only three wires are needed:
Which signal a device listens to for incoming data and which signal it actively sends data is very confusing. If the device is a "DTE" (Data Terminating Equipment) it transmits on TX and listens on RX. If the device is "Data Communicating Equipment" (e.g. a modem) it listens for data on TX and transmits on RX.
The standard speeds range from 75 buad up to 115,200 baud, with 9600 or 19200 being the most common speeds for data that is presented to people (such as on a serial console).
As well as baud speed both ends of a connection must be using the same frame parameters - the most common being one start bit, eight data bits, no parity bit and one stop bit. As the frame is ten bits long at 9600 baud you can send 960 bytes per second.
There is a whole lot more to the standard, mostly around how senders and receivers control the flow of data to ensure that data does not overrun receiving buffers. When using modern hardware at slow speeds handshaking isn't really an issue.
Here's what it looks like on the wire:
Generating an RS-232 signal
For this project we need a shift register (well two actually). So what does a shift register look like in VHDL?
Here's a 16 bit register that loops from bit 0 to bit 15 - a much easy way to generate one pulse every 16 cycles than using a counter
... signal shiftreg : std_logic_vector(15 downto 0) := "0000000000000001"; ... if rising_edge(clk) then shiftreg <= shiftreg(0) & shiftreg(15 downto 1); end if;
For RS-252 we use pretty much this construct, but feed in the the idle bit value ('1'). This code will send the 'Z' character once (after which the shift register is filled with '1's):
... signal shiftreg : std_logic_vector(9 downto 0) := "1010110100"; ... data_out <= shiftreg(0); ... if rising_edge(clk) then shiftreg <= '1' & shiftreg(9 downto 1) end if;
The user data is bits 8 downto 1 is the "byte" of user data - bit 0 is the start bit, and bit 9 is the stop bit. I chose 'Z' as it will still be a 'Z' regardless of if the least or most significant bit gets transferred first - very useful for testing!
The only problem with the code so far is that we are transmitting at the clock speed - 50,000,000 baud! To control the rate of sending we also need a counter that allows a bit to be sent at 9600 baud - once every 5208 clock cycles:
... signal shiftreg : std_logic_vector(9 downto 0) := "1010110100"; signal counter : std_logic_vector(12 downto 0) := (others => '0'); ... data_out <= shiftreg(0); ... if rising_edge(clk) then if counter = 5207 then shiftreg <= '1' & shiftreg(9 downto 1); counter <= (others => '0'); else counter <= counter+1; end if; end if;
We can make it send the same data over and over again by making the shift register longer and looping the shift register's output back on its input. T do this it needs longer shift register, ensuring that we have some quiet space following the stop bit to allow the receiver to frame the data correctly:
... signal shiftreg : std_logic_vector(15 downto 0) := "1111111010110100"; signal counter : std_logic_vector(12 downto 0) := (others => '0'); ... data_out <= shiftreg(0); ... if rising_edge(clk) then if counter = 5207 then shiftreg <= shiftreg(0) & shiftreg(15 downto 1); counter <= (others => '0'); else counter <= counter+1; end if; end if;
(This code should be enough to enable you to test your RS232 port actually sends data as expected).
Sending variable data
To make this useful you really need to be able to send different data bytes. to do this you have to know when the interface is busy.
The easiest way to do this is to have a second shift register which is filled with '1's when the character is loaded into 'shiftreg' and filled with '0's as bits are transmitted. Once this second shift register is all zeros, then things are ready for the next byte to be sent:
... signal busyshiftreg : std_logic_vector(9 downto 0) := (others => '0'); signal datashiftreg : std_logic_vector(9 downto 0) := (others => '1'); signal counter : std_logic_vector(12 downto 0) := (others => '0'); ... data_out <= datashiftreg(0); busy_out <= busyshiftreg(0); ... if rising_edge(clk) then if busyshiftreg(0) = '0' then busyshiftreg <= (others => '1'); datashiftreg <= '1' & databyte & '0'; counter <= (others <= '0'); else if counter = 5207 then datashiftreg <= '1' & datashiftreg(9 downto 1); busyshiftreg <= '0' & busyshiftreg(9 downto 1); counter <= (others => '0'); else counter <= counter+1; end if; end if; end if;
The important bit is to remember to reset 'counter' when a new byte is loaded into 'datashiftreg'. Failing to do this will cause the start bit to be of different lengths - the project will work correctly when streaming bytes to the host, but will sometimes get garbage for the first few bytes of a message until it recovers from the bad bit.
Connecting to a PC
Connecting the FPGA directly to your serial port will most likely ruin your FPGA```
Most modern PCs do not have RS232 ports, and if they do they are expecting the higher voltage levels that standard RS232 uses - the standards uses up to +/- 25V!
To connect to a PC over USB you can use something like Sparkfun's "FTDI Basic 3.3V - USB to Serial" (http://www.sparkfun.com/products/9893) and wire jumpers. Here's my setup:
To talk to a true standards compliant RS-232 port, or if you want to avoid issues caused by loose wires you can use the RS-232 PMOD http://www.digilentinc.com/Products/Detail.cfm?Prod=PMOD-RS232 with your Basys2.
- Create a project that sends 'Z' over RS-232
- Create a project that sends the state of switches(3 downto 0) over RS-232
- You could increase the length of the shift register and send multiple bytes
- You could convert the data to ASCII and send four switches in a single byte
- You could map the 16 possible values into 16 contiguous printable characters (A-Q perhaps)
- Change it to only send a byte when the switches change.
- Extend the project to send the state of all eight switches
- What would happen if a was to toggle and then change back to its original state in less than 1/960th of a second? Can loss of data be avoided?
Ready to carry on?
Click here to carry on to the next module.