My first FPGA – UART-Communication
|My first steps in the world of FPGAs I did last week when I tried to implement an UART-interface to communicate with my PC. Because my DE0-Nano did not contain a display, I decided to use the 8 green LED on the board to show the received data.
Of course, there are a lot of examples out there to start with an FPGA and I really used a lot of different blogs to analyze examples to learn from, but it doesn’t mean, another “my-first-FPGA”-example is unnecessary.
I use a SparkFun FTDI Basic Breakout with 3.3V signals, because I was not sure, whether the used GPIO-pins are 5V-tolerant.
The code is segmented into 3 files. One for the main-entity (UART) and two others for the RX- and TX-component:
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; ENTITY UART IS PORT ( CLOCK_50: IN STD_LOGIC; KEY: IN STD_LOGIC_VECTOR(1 downto 0); LED: OUT STD_LOGIC_VECTOR(7 downto 0); UART_RXD: IN STD_LOGIC; UART_TXD: OUT STD_LOGIC ); END UART; ARCHITECTURE main OF UART IS SIGNAL tx_start: STD_LOGIC:='0'; SIGNAL tx_busy: STD_LOGIC; SIGNAL tx_data: STD_LOGIC_VECTOR(7 DOWNTO 0); SIGNAL rx_busy: STD_LOGIC; SIGNAL rx_data: STD_LOGIC_VECTOR(7 DOWNTO 0); COMPONENT tx PORT( clk: IN STD_LOGIC; start: IN STD_LOGIC; busy: OUT STD_LOGIC; data: IN STD_LOGIC_VECTOR(7 downto 0); tx_line: OUT STD_LOGIC ); END COMPONENT tx; COMPONENT rx PORT( clk: IN STD_LOGIC; busy: OUT STD_LOGIC; data: OUT STD_LOGIC_VECTOR(7 downto 0); rx_line: IN STD_LOGIC ); END COMPONENT rx; BEGIN C1: tx PORT MAP(CLOCK_50, tx_start, tx_busy, tx_data, UART_TXD); C2: rx PORT MAP(CLOCK_50, rx_busy, rx_data, UART_RXD); PROCESS(rx_busy) BEGIN IF falling_edge(rx_busy) THEN LED<=rx_data; END IF; END PROCESS; PROCESS(CLOCK_50) BEGIN IF rising_edge(CLOCK_50) THEN IF (KEY(0)='0' AND tx_busy='0') THEN tx_data<="00010001"; tx_start<='1'; --LED<=tx_data; ELSE tx_start<='0'; END IF; END IF; END PROCESS; END ARCHITECTURE main;
The RX-component look like that:
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; ENTITY rx IS PORT( clk: IN STD_LOGIC; busy: OUT STD_LOGIC; data: OUT STD_LOGIC_VECTOR(7 downto 0); rx_line: IN STD_LOGIC ); END rx; ARCHITECTURE main OF rx IS SIGNAL prescaler: INTEGER RANGE 0 TO 434:=0; SIGNAL index: INTEGER RANGE 0 TO 9:=0; SIGNAL fullData: STD_LOGIC_VECTOR (9 downto 0); SIGNAL rx_flag: STD_LOGIC:='0'; BEGIN PROCESS(clk) BEGIN IF rising_edge(clk) THEN IF(rx_flag='0' AND rx_line='0') THEN busy<='1'; index<=0; prescaler<=0; rx_flag<='1'; END IF; IF (rx_flag='1') THEN fullData(index)<=rx_line; IF (prescaler<433) THEN prescaler<=prescaler+1; ELSE prescaler<=0; END IF; IF (prescaler=216) THEN IF (index<9) THEN index<=index+1; ELSE IF (fullData(0)='0' AND fullData(9)='1') THEN data<=fullData(8 DOWNTO 1); ELSE data<=(others=>'0'); END IF; rx_flag<='0'; busy<='0'; END IF; END IF; END IF; END IF; END PROCESS; END ARCHITECTURE main;
and the TX-component looks very similar:
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; ENTITY tx IS PORT( clk: IN STD_LOGIC; start: IN STD_LOGIC; busy: OUT STD_LOGIC; data: IN STD_LOGIC_VECTOR(7 downto 0); tx_line: OUT STD_LOGIC ); END tx; ARCHITECTURE main OF tx IS SIGNAL prescaler: INTEGER RANGE 0 TO 434:=0; SIGNAL index: INTEGER RANGE 0 TO 9:=0; SIGNAL fullData: STD_LOGIC_VECTOR (9 downto 0); SIGNAL tx_flag: STD_LOGIC:='0'; BEGIN PROCESS(clk) BEGIN IF rising_edge(clk) THEN IF (tx_flag='0' AND start='1') THEN tx_flag<='1'; busy<='1'; fullData(0)<='0'; fullData(9)<='1'; fullData(8 DOWNTO 1)<=data; END IF; IF (tx_flag='1') THEN IF (prescaler<433) THEN prescaler<=prescaler+1; ELSE prescaler<=0; END IF; IF (prescaler=216) THEN tx_line<=fullData(index); IF (index<9) THEN index<=index+1; ELSE tx_flag<='0'; busy<='0'; index<=0; END IF; END IF; END IF; END IF; END PROCESS; END ARCHITECTURE main;
After programming the Cyclone IV the LED are all off. After sending a 8-bit binary from HTerm the corresponding LED are on now.