Instead of unsigned, use numeric library. Asynchronous serial data is asynchronous to your FPGA baud clock, so YOU have to do the synchronization. That means, you have to use oversampling, like 8 times, 16 times baud rate (usually power of 2, makes live easier). Then you search for your start condition, and if you find it you reset your counter. From there on your counter is synchronous to the incoming data and overflows on every new bit. Now sampling (reading) your data at that moment is not wise, bc there, your serial data is toggling. To read the stabilized data, you should wait a little, aiming for the middle to create a "centered interface".
Code VHDL - [expand] |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
| library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity receiver is
port(
Reset : in std_logic; -- assynchronous reset
Enable : in std_logic; -- add an enable?
SYS_CLK : in std_logic; -- system clock - you will probably not want to run your complete FPGA-Design on the same freq. like baud rate?
UART_CLK : in std_logic; -- 16 x baud
-- receiver
SIN : in std_logic; -- serial in
RX_Rd : in std_logic; -- read your received bytes at system clock
RX_Empty : out std_logic; -- receiver empty - there are no received byte data for your FPGA design to process :(
RX_Data_Out : out std_logic_vector(7 downto 0)
);
end entity;
architecture arc of receiver is
signal iSIN_t1 : std_logic; -- serial data, delayed version
signal iSIN_t2 : std_logic; -- serial data, delayed version
begin
process(UART_CLK, Reset)
begin
if Reset='0' then
iSOF <= '0'; -- no start of frame
iSIN_t1 <= '1'; -- uart is idle high!
iSIN_t2 <= '1'; -- uart is idle high!
elsif rising_edge(UART_CLK) then
-- synchronize the assynchronous UART RX with your UART_CLK
iSIN_t1 <= SIN; -- shift in your serial data
iSIN_t2 <= iSIN_t1; -- shift again
if enable = '1' then
-- if no start of frame detected, find it!
if iSIN_t2 = '0' and iSOF = '0' then
iSOF <= '1'; -- there, you found it!
iCLK_CNT <= 0; -- initialize your counter
end if;
if iSOF = '1' then
iCLK_CNT <= iCLK_CNT + 1; -- keep counting clock cycles
if iCLK_CNT = 7 then -- UART CLK is 16 x baud, remember?
-- sample SIN at the center of baud clock
-- so we initialized clk counter at start of frame,
-- and keep counting to find the middle between clock period
end if;
-- check for errors
-- save your byte
end if;
end if ;
end if;
end process;
-- save your byte
-- to use it with your system clock of the FPGA design,
-- you need to synchronize between the two clock domains
-- us a small FIFO for this, 8 / 16 bytes depth is enough
-- you can use a core generator to generate the fifo
rx_fifo_buffer : fifo_dc
port map (
WrCLK => UART_CLK,
RdCLK => SYS_CLK,
Din => iUART_BYTE_DATA,
Dout => RX_Data_Out,
Empty => RX_Empty
);
end arc; |
That is enough to get you started. There are lots of information regarding this topic. Have a closer look on the timing diagrams to understand serial transmission. It is usually easier to start with the transmitter, if you need that as well. simulate it!
Just as a sidenote, uart communication can tolerate +- 6% baud rate in theory (almost). In practice it means that your communication still works if you are using a clock that is 3% off the actual baud rate.
This is helpful if you have difficulties to create the correct baud rate on your fpga, especially for support of various baudrates.