forked from antonblanchard/microwatt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sim_16550_uart.vhdl
421 lines (378 loc) · 14.6 KB
/
sim_16550_uart.vhdl
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.sim_console.all;
entity uart_top is
port(
wb_clk_i : in std_ulogic;
wb_rst_i : in std_ulogic;
wb_adr_i : in std_ulogic_vector(2 downto 0);
wb_dat_i : in std_ulogic_vector(7 downto 0);
wb_dat_o : out std_ulogic_vector(7 downto 0);
wb_we_i : in std_ulogic;
wb_stb_i : in std_ulogic;
wb_cyc_i : in std_ulogic;
wb_ack_o : out std_ulogic;
int_o : out std_ulogic;
stx_pad_o : out std_ulogic;
srx_pad_i : in std_ulogic;
rts_pad_o : out std_ulogic;
cts_pad_i : in std_ulogic;
dtr_pad_o : out std_ulogic;
dsr_pad_i : in std_ulogic;
ri_pad_i : in std_ulogic;
dcd_pad_i : in std_ulogic
);
end entity uart_top;
architecture behaviour of uart_top is
-- Call POLL every N clocks to generate interrupts
constant POLL_DELAY : natural := 100;
-- Register definitions
subtype reg_adr_t is std_ulogic_vector(2 downto 0);
constant REG_IDX_RXTX : reg_adr_t := "000";
constant REG_IDX_IER : reg_adr_t := "001";
constant REG_IDX_IIR_FCR : reg_adr_t := "010";
constant REG_IDX_LCR : reg_adr_t := "011";
constant REG_IDX_MCR : reg_adr_t := "100";
constant REG_IDX_LSR : reg_adr_t := "101";
constant REG_IDX_MSR : reg_adr_t := "110";
constant REG_IDX_SCR : reg_adr_t := "111";
-- IER bits
constant REG_IER_RDI_BIT : natural := 0;
constant REG_IER_THRI_BIT : natural := 1;
constant REG_IER_RLSI_BIT : natural := 2;
constant REG_IER_MSI_BIT : natural := 3;
-- IIR bit
constant REG_IIR_NO_INT : natural := 0;
-- IIR values for bit 3 downto 0
constant REG_IIR_RDI : std_ulogic_vector(3 downto 1) := "010";
constant REG_IIR_THRI : std_ulogic_vector(3 downto 1) := "001";
constant REG_IIR_RLSI : std_ulogic_vector(3 downto 1) := "011";
constant REG_IIR_MSI : std_ulogic_vector(3 downto 1) := "000";
-- FCR bits
constant REG_FCR_EN_FIFO_BIT : natural := 0; -- Always 1
constant REG_FCR_CLR_RCVR_BIT : natural := 1;
constant REG_FCR_CLR_XMIT_BIT : natural := 2;
constant REG_FCR_DMA_SEL_BIT : natural := 3; -- Not implemented
-- FCR values for FIFO threshold in bits 7 downto 6
constant REG_FCR_FIFO_TRIG1 : std_ulogic_vector(7 downto 6) := "00";
constant REG_FCR_FIFO_TRIG4 : std_ulogic_vector(7 downto 6) := "01";
constant REG_FCR_FIFO_TRIG8 : std_ulogic_vector(7 downto 6) := "10";
constant REG_FCR_FIFO_TRIG14 : std_ulogic_vector(7 downto 6) := "11";
-- LCR bits
constant REG_LCR_STOP_BIT : natural := 2;
constant REG_LCR_PARITY_BIT : natural := 3;
constant REG_LCR_EPAR_BIT : natural := 4;
constant REG_LCR_SPAR_BIT : natural := 5;
constant REG_LCR_SBC_BIT : natural := 6;
constant REG_LCR_DLAB_BIT : natural := 7;
-- LCR values for data length (bits 1 downto 0)
constant REG_LCR_WLEN5 : std_ulogic_vector(1 downto 0) := "00";
constant REG_LCR_WLEN6 : std_ulogic_vector(1 downto 0) := "01";
constant REG_LCR_WLEN7 : std_ulogic_vector(1 downto 0) := "10";
constant REG_LCR_WLEN8 : std_ulogic_vector(1 downto 0) := "11";
-- MCR bits
constant REG_MCR_DTR_BIT : natural := 0;
constant REG_MCR_RTS_BIT : natural := 1;
constant REG_MCR_OUT1_BIT : natural := 2;
constant REG_MCR_OUT2_BIT : natural := 3;
constant REG_MCR_LOOP_BIT : natural := 4;
-- LSR bits
constant REG_LSR_DR_BIT : natural := 0;
constant REG_LSR_OE_BIT : natural := 1;
constant REG_LSR_PE_BIT : natural := 2;
constant REG_LSR_FE_BIT : natural := 3;
constant REG_LSR_BI_BIT : natural := 4;
constant REG_LSR_THRE_BIT : natural := 5;
constant REG_LSR_TEMT_BIT : natural := 6;
constant REG_LSR_FIFOE_BIT : natural := 7;
-- MSR bits
constant REG_MSR_DCTS_BIT : natural := 0;
constant REG_MSR_DDSR_BIT : natural := 1;
constant REG_MSR_TERI_BIT : natural := 2;
constant REG_MSR_DDCD_BIT : natural := 3;
constant REG_MSR_CTS_BIT : natural := 4;
constant REG_MSR_DSR_BIT : natural := 5;
constant REG_MSR_RI_BIT : natural := 6;
constant REG_MSR_DCD_BIT : natural := 7;
-- Wishbone signals decode:
signal reg_idx : reg_adr_t;
signal wb_phase : std_ulogic;
signal reg_write : std_ulogic;
signal reg_read : std_ulogic;
-- Register storage
signal reg_ier : std_ulogic_vector(3 downto 0);
signal reg_iir : std_ulogic_vector(3 downto 0);
signal reg_fcr : std_ulogic_vector(7 downto 6);
signal reg_lcr : std_ulogic_vector(7 downto 0);
signal reg_mcr : std_ulogic_vector(4 downto 0);
signal reg_lsr : std_ulogic_vector(7 downto 0);
signal reg_msr : std_ulogic_vector(7 downto 0);
signal reg_scr : std_ulogic_vector(7 downto 0);
signal reg_div : std_ulogic_vector(15 downto 0);
-- Control signals
signal rx_fifo_clr : std_ulogic;
signal tx_fifo_clr : std_ulogic;
-- Pending interrupts
signal int_rdi_pending : std_ulogic;
signal int_thri_pending : std_ulogic;
signal int_rlsi_pending : std_ulogic;
signal int_msi_pending : std_ulogic;
-- Actual data output
signal data_out : std_ulogic_vector(7 downto 0) := x"00";
-- Incoming data pending signal
signal data_in_pending : std_ulogic := '0';
-- Useful aliases
alias dlab : std_ulogic is reg_lcr(REG_LCR_DLAB_BIT);
alias clk : std_ulogic is wb_clk_i;
alias rst : std_ulogic is wb_rst_i;
alias cyc : std_ulogic is wb_cyc_i;
alias stb : std_ulogic is wb_stb_i;
alias we : std_ulogic is wb_we_i;
begin
-- Register index shortcut
reg_idx <= wb_adr_i(2 downto 0);
-- 2 phases WB process.
--
-- Among others, this gives us a "free" cycle for the
-- side effects of some accesses percolate in the form
-- of status bit changes in other registers.
wb_cycle: process(clk)
variable phase : std_ulogic := '0';
begin
if rising_edge(clk) then
if wb_phase = '0' then
if cyc = '1' and stb = '1' then
wb_ack_o <= '1';
wb_phase <= '1';
end if;
else
wb_ack_o <= '0';
wb_phase <= '0';
end if;
end if;
end process;
-- Reg read/write signals
reg_write <= cyc and stb and we and not wb_phase;
reg_read <= cyc and stb and not we and not wb_phase;
-- Register read is synchronous to avoid collisions with
-- read-clear side effects
do_reg_read: process(clk)
begin
if rising_edge(clk) then
wb_dat_o <= x"00";
if reg_read = '1' then
case reg_idx is
when REG_IDX_RXTX =>
if dlab = '1' then
wb_dat_o <= reg_div(7 downto 0);
else
wb_dat_o <= data_out;
end if;
when REG_IDX_IER =>
if dlab = '1' then
wb_dat_o <= reg_div(15 downto 8);
else
wb_dat_o <= "0000" & reg_ier;
end if;
when REG_IDX_IIR_FCR =>
-- Top bits always set as FIFO is always enabled
wb_dat_o <= "1100" & reg_iir;
when REG_IDX_LCR =>
wb_dat_o <= reg_lcr;
when REG_IDX_LSR =>
wb_dat_o <= reg_lsr;
when REG_IDX_MSR =>
wb_dat_o <= reg_msr;
when REG_IDX_SCR =>
wb_dat_o <= reg_scr;
when others =>
end case;
end if;
end if;
end process;
-- Receive/send synchronous process
rxtx: process(clk)
variable dp : std_ulogic;
variable poll_cnt : natural;
variable sim_tmp : std_ulogic_vector(63 downto 0);
begin
if rising_edge(clk) then
if rst = '0' then
dp := data_in_pending;
if dlab = '0' and reg_idx = REG_IDX_RXTX then
if reg_write = '1' then
-- FIFO write
-- XXX Simulate the FIFO and delays for more
-- accurate behaviour & interrupts
sim_console_write(x"00000000000000" & wb_dat_i);
end if;
if reg_read = '1' then
dp := '0';
data_out <= x"00";
end if;
end if;
-- Poll for incoming data
if poll_cnt = 0 or (reg_read = '1' and reg_idx = REG_IDX_LSR) then
sim_console_poll(sim_tmp);
poll_cnt := POLL_DELAY;
if dp = '0' and sim_tmp(0) = '1' then
dp := '1';
sim_console_read(sim_tmp);
data_out <= sim_tmp(7 downto 0);
end if;
poll_cnt := poll_cnt - 1;
end if;
data_in_pending <= dp;
end if;
end if;
end process;
-- Interrupt pending bits
int_rdi_pending <= data_in_pending;
int_thri_pending <= '1';
int_rlsi_pending <= reg_lsr(REG_LSR_OE_BIT) or
reg_lsr(REG_LSR_PE_BIT) or
reg_lsr(REG_LSR_FE_BIT) or
reg_lsr(REG_LSR_BI_BIT);
int_msi_pending <= reg_msr(REG_MSR_DCTS_BIT) or
reg_msr(REG_MSR_DDSR_BIT) or
reg_msr(REG_MSR_TERI_BIT) or
reg_msr(REG_MSR_DDCD_BIT);
-- Derive interrupt output from IIR
int_o <= not reg_iir(REG_IIR_NO_INT);
-- Divisor register
div_reg_w: process(clk)
begin
if rising_edge(clk) then
if rst = '1' then
reg_div <= (others => '0');
elsif reg_write = '1' and dlab = '1' then
if reg_idx = REG_IDX_RXTX then
reg_div(7 downto 0) <= wb_dat_i;
elsif reg_idx = REG_IDX_IER then
reg_div(15 downto 8) <= wb_dat_i;
end if;
end if;
end if;
end process;
-- IER register
ier_reg_w: process(clk)
begin
if rising_edge(clk) then
if rst = '1' then
reg_ier <= "0000";
else
if reg_write = '1' and dlab = '0' and reg_idx = REG_IDX_IER then
reg_ier <= wb_dat_i(3 downto 0);
end if;
end if;
end if;
end process;
-- IIR (read only) generation
iir_reg_w: process(clk)
begin
if rising_edge(clk) then
reg_iir <= "0001";
if int_rlsi_pending = '1' and reg_ier(REG_IER_RLSI_BIT) = '1' then
reg_iir <= REG_IIR_RLSI & "0";
elsif int_rdi_pending = '1' and reg_ier(REG_IER_RDI_BIT) = '1' then
reg_iir <= REG_IIR_RDI & "0";
elsif int_thri_pending = '1' and reg_ier(REG_IER_THRI_BIT) = '1' then
reg_iir <= REG_IIR_THRI & "0";
elsif int_msi_pending = '1' and reg_ier(REG_IER_MSI_BIT) = '1' then
reg_iir <= REG_IIR_MSI & "0";
end if;
-- It *seems* like reading IIR should clear THRI for
-- some amount of time until it gets set again a few
-- clocks later if the transmitter is still empty. We
-- don't do that at this point.
end if;
end process;
-- FCR (write only) register
fcr_reg_w: process(clk)
begin
if rising_edge(clk) then
if rst = '1' then
reg_fcr <= "11";
rx_fifo_clr <= '1';
tx_fifo_clr <= '1';
elsif reg_write = '1' and reg_idx = REG_IDX_IIR_FCR then
reg_fcr <= wb_dat_i(7 downto 6);
rx_fifo_clr <= wb_dat_i(REG_FCR_CLR_RCVR_BIT);
tx_fifo_clr <= wb_dat_i(REG_FCR_CLR_XMIT_BIT);
else
rx_fifo_clr <= '0';
tx_fifo_clr <= '0';
end if;
end if;
end process;
-- LCR register
lcr_reg_w: process(clk)
begin
if rising_edge(clk) then
if rst = '1' then
reg_lcr <= "00000011";
elsif reg_write = '1' and reg_idx = REG_IDX_LCR then
reg_lcr <= wb_dat_i;
end if;
end if;
end process;
-- MCR register
mcr_reg_w: process(clk)
begin
if rising_edge(clk) then
if rst = '1' then
reg_mcr <= "00000";
elsif reg_write = '1' and reg_idx = REG_IDX_MCR then
reg_mcr <= wb_dat_i(4 downto 0);
end if;
end if;
end process;
-- LSR register
lsr_reg_w: process(clk)
begin
if rising_edge(clk) then
if rst = '1' then
reg_lsr <= "00000000";
else
reg_lsr(REG_LSR_DR_BIT) <= data_in_pending;
-- Clear error bits on read. Those bits are
-- always 0 in sim for now.
-- if reg_read = '1' and reg_idx = REG_IDX_LSR then
-- reg_lsr(REG_LSR_OE_BIT) <= '0';
-- reg_lsr(REG_LSR_PE_BIT) <= '0';
-- reg_lsr(REG_LSR_FE_BIT) <= '0';
-- reg_lsr(REG_LSR_BI_BIT) <= '0';
-- reg_lsr(REG_LSR_FIFOE_BIT) <= '0';
-- end if;
-- Tx FIFO empty indicators. Always empty in sim
reg_lsr(REG_LSR_THRE_BIT) <= '1';
reg_lsr(REG_LSR_TEMT_BIT) <= '1';
end if;
end if;
end process;
-- MSR register
msr_reg_w: process(clk)
begin
if rising_edge(clk) then
if rst = '1' then
reg_msr <= "00000000";
elsif reg_read = '1' and reg_idx = REG_IDX_MSR then
reg_msr <= "00000000";
-- XXX TODO bit setting machine...
end if;
end if;
end process;
-- SCR register
scr_reg_w: process(clk)
begin
if rising_edge(clk) then
if rst = '1' then
reg_scr <= "00000000";
elsif reg_write = '1' and reg_idx = REG_IDX_SCR then
reg_scr <= wb_dat_i;
end if;
end if;
end process;
end architecture behaviour;