-
Notifications
You must be signed in to change notification settings - Fork 2
/
fakeuart.vhd
155 lines (134 loc) · 3.72 KB
/
fakeuart.vhd
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
package uart_comp is
component uart
Port(
WR_D : in std_logic_vector(7 downto 0);
RD_D : out std_logic_vector(7 downto 0);
ABUS : in std_logic_vector(15 downto 0);
WR_EN : in std_logic;
INT : out std_logic;
TX : out std_logic;
RX : in std_logic;
CLK : in std_logic;
RST : in std_logic );
end component;
end package;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
library UNISIM;
use UNISIM.VComponents.all;
-- FIXME: How does starting, stopping and changing the uart affect
-- the phase of the clock divider, if at all?
use work.uart_comp.all;
entity uart is
Port(
WR_D : in std_logic_vector(7 downto 0);
RD_D : out std_logic_vector(7 downto 0);
ABUS : in std_logic_vector(15 downto 0);
WR_EN : in std_logic;
INT : out std_logic;
TX : out std_logic;
RX : in std_logic;
CLK : in std_logic;
RST : in std_logic );
end uart;
architecture Behaviour of uart is
-- Registers
signal sb, sb_new : std_logic_vector(7 downto 0);
signal sc, sc_new : std_logic_vector(7 downto 0);
signal shift, shift_new : std_logic_vector(7 downto 0);
signal bitcount, bitcount_new : unsigned(3 downto 0);
-- Baudrate divider
-- Input clock is master CPU clock at 100 MHz / 24
-- Dividing by 4 gives a rate of 1041666.6 Baud
-- FT232R chip can achieve 1043478.261 Baud for a divider ratio of 2.875, an error of 0.17%
signal div, div_new : unsigned(1 downto 0);
-- FSM
type STATES is (SLEEP, TXMIT);
signal CS, NS : STATES;
-- OUTPUT
signal tx_int, tx_new : std_logic;
begin
RD_D <= "ZZZZZZZZ" when WR_EN = '1' else
sb when ABUS = X"FF01" else
sc when ABUS = X"FF02" else
"ZZZZZZZZ";
TX <= tx_int;
latchproc : process(CLK, RST)
begin
if RST = '1' then
sb <= (others => '0');
sc <= (others => '0');
shift <= (others => '0');
CS <= SLEEP;
tx_int <= '1';
div <= (others => '0');
bitcount <= (others => '0');
elsif rising_edge(CLK) then
sb <= sb_new;
sc <= sc_new;
shift <= shift_new;
CS <= NS;
tx_int <= tx_new;
div <= div_new;
bitcount <= bitcount_new;
end if;
end process;
combproc : process(sb, sc, shift, CS, tx_int, div, bitcount, ABUS, WR_D, WR_EN)
variable sb_nxt, sc_nxt, shift_nxt : std_logic_vector(7 downto 0);
variable STEP : STATES;
variable tx_nxt : std_logic;
variable div_nxt : unsigned(1 downto 0);
variable bitcount_nxt : unsigned(3 downto 0);
begin
sb_nxt := sb;
sc_nxt := sc;
shift_nxt := shift;
STEP := CS;
tx_nxt := tx_int;
div_nxt := div + "1";
bitcount_nxt := bitcount;
if CS = TXMIT and div = "0" then
if bitcount_nxt = "1000" then -- Time to send the stop bit
tx_nxt := '1';
bitcount_nxt := "1001";
elsif bitcount_nxt = "1001" then -- Done transmitting
STEP := SLEEP;
sc_nxt(7) := '0';
sb_nxt := shift;
else -- Shifting out data
shift_nxt := '0' & shift(7 downto 1);
tx_nxt := shift(0);
bitcount_nxt := bitcount + "1";
end if;
end if;
-- FIXME is writing during a transmission allowed, and does it have an effect?
if WR_EN = '1' then
if ABUS = X"FF01" then
sb_nxt := WR_D;
elsif ABUS = X"FF02" then
sc_nxt := WR_D;
-- A '1' in the MSb starts transmission
if WR_D(7) = '1' then
STEP := TXMIT;
-- Start bit
tx_nxt := '0';
div_nxt := "10"; -- Start at 2 rather than 1 because write takes 2 cycles, or start at 1 and check CS
bitcount_nxt := (others => '0');
shift_nxt := sb;
else
STEP := SLEEP;
end if;
end if;
end if;
sb_new <= sb_nxt;
sc_new <= sc_nxt;
shift_new <= shift_nxt;
NS <= STEP;
tx_new <= tx_nxt;
div_new <= div_nxt;
bitcount_new <= bitcount_nxt;
end process;
end Behaviour;