-
Notifications
You must be signed in to change notification settings - Fork 6
/
mesa_uart.v
482 lines (423 loc) · 14.8 KB
/
mesa_uart.v
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
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
/* ****************************************************************************
-- (C) Copyright 2015 Kevin M. Hubbard - Black Mesa Labs
-- Source file: mesa_uart.v
-- Date: June 1, 2015
-- Author: khubbard
-- Description: A UART that autobauds to first "\n" 0x0A character.
-- Language: Verilog-2001
-- License: This project is licensed with the CERN Open Hardware Licence
-- v1.2. You may redistribute and modify this project under the
-- terms of the CERN OHL v.1.2. (http://ohwr.org/cernohl).
-- This project is distributed WITHOUT ANY EXPRESS OR IMPLIED
-- WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY
-- AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN OHL
-- v.1.2 for applicable Conditions.
-- License: This project is licensed with the CERN Open Hardware Licence
-- v1.2. You may redistribute and modify this project under the
-- terms of the CERN OHL v.1.2. (http://ohwr.org/cernohl).
-- This project is distributed WITHOUT ANY EXPRESS OR IMPLIED
-- WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY
-- AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN OHL
-- v.1.2 for applicable Conditions.
--
-- RXD \START/<D0><D1><D2><..><D7>/STOP
-- Design Statistics after Packing
-- en_clr_lock = 1;
-- Number of LUTs : 190 / 384
-- Number of DFFs : 110 / 384
--
-- Revision History:
-- Ver# When Who What
-- ---- -------- -------- ---------------------------------------------------
-- 0.1 06.01.15 khubbard Creation
-- 0.2 08.18.16 khubbard rx_sr[2:0] fix for metastable sampling issue.
-- ***************************************************************************/
//`default_nettype none // Strictly enforce all nets to be declared
module mesa_uart
(
input wire reset,
input wire clk,
input wire clr_baudlock,
input wire en_autobaud,
output wire flush,
input wire rxd,
output reg txd,
output reg [7:0] dbg,
output wire [7:0] rx_byte,
output reg rx_rdy,
input wire [7:0] tx_byte,
input wire tx_en,
output reg tx_busy,
output reg tx_idle,
output wire rx_idle,
output wire [15:0] baud_rate,
output wire baud_lock
); // module mesa_uart
reg [8:0] rx_byte_sr;
reg [3:0] rx_bit_cnt;
reg [3:0] tx_bit_cnt;
reg sample_now;
reg sample_now_p1;
reg rxd_meta;
reg rxd_loc;
wire rxd_sample;
reg [7:0] rxd_sr;
reg clr_bit_lock;
reg dbg_sample_togl;
reg dbg_sample_togl_p1;
reg dbg_byte_togl;
reg clr_jk;
reg bad_jk;
reg bit_lock_jk;
reg bit_lock_jk_p1;
reg baud_lock_jk;
reg rx_byte_jk;
reg rx_byte_jk_p1;
wire [7:0] rx_byte_loc;
reg [15:0] rx_cnt_16b;
reg [15:0] tx_cnt_16b;
reg [15:0] baud_rate_loc;
reg rx_cnt_16b_p1;
reg rx_cnt_16b_roll;
reg rx_start_a;
reg rx_start_b;
reg rx_cnt_en_a_jk;
reg rx_cnt_en_b;
reg rx_rdy_loc;
reg rx_rdy_pre;
wire en_loopback;
wire en_fast_autobaud;
reg [7:0] tx_byte_loc;
reg tx_en_loc;
reg [9:0] tx_sr;
reg txd_loc;
reg rxd_fal;
reg rxd_ris;
reg [7:0] rxd_fal_sr;
reg tx_shift;
reg tx_now;
reg rx_ascii_cr;
reg rx_bad_symbol;
reg [7:0] dbg_loc;
reg [7:0] dbg_loc_p1;
reg [3:0] dbg_cnt;
reg sample_first;
// Send RXD to TXD to test circuit
assign en_loopback = 0;
//assign en_fast_autobaud = 0;
assign en_fast_autobaud = 1;
assign rx_idle = 0;
// When baud_lock asserts, send "\n" to downstream nodes for detection
assign baud_rate = baud_rate_loc[15:0];
assign baud_lock = baud_lock_jk;
//-----------------------------------------------------------------------------
// Output some debug signals for Saleae. Pulse extend short events
//-----------------------------------------------------------------------------
always @ ( posedge clk ) begin : proc_dbg
begin
// dbg_loc[0] <= ~ rxd_meta;
dbg_loc[0] <= rxd_sample;
dbg_loc[1] <= txd_loc;
dbg_loc[2] <= bit_lock_jk;
dbg_loc[3] <= baud_lock_jk;
// dbg_loc[4] <= sample_now;
dbg_loc[4] <= reset;
// dbg_loc[5] <= rx_rdy_loc;
dbg_loc[5] <= clr_jk;
dbg_loc[6] <= dbg_sample_togl;
dbg_loc[7] <= dbg_byte_togl;
// dbg_loc[7] <= bad_jk;
// dbg_loc[7] <= rx_ascii_cr;
dbg_loc_p1 <= dbg_loc[7:0];
// Extend transitions for Saleae
if ( dbg_cnt != 4'hF ) begin
dbg_cnt <= dbg_cnt + 1;
end
if ( dbg_loc[7:4] != dbg_loc_p1[7:4] && dbg_cnt == 4'hF ) begin
dbg <= dbg_loc[7:0];
dbg_cnt <= 4'h0;
end
if ( reset == 1 ) begin
dbg_cnt <= 4'h0;
end
dbg[0] <= dbg_loc[0];
dbg[1] <= dbg_loc[1];
dbg[2] <= dbg_loc[2];
dbg[3] <= dbg_loc[3];
end
end // proc_din
//-----------------------------------------------------------------------------
// Asynchronous sampling of RXD into 4 bit Shift Register
// Note: RXD is immediately inverted to prevent runt '0's from pipeline
// being detected as start bits. FPGA Flops powerup to '0', so inverting fixes.
//-----------------------------------------------------------------------------
always @ ( posedge clk ) begin : proc_din
begin
rxd_meta <= ~ rxd;// Note Inversion to prevent runt post config
rxd_loc <= rxd_meta;
// rxd_sr[7:0] <= { rxd_sr[6:0], rxd_meta };
rxd_sr[7:0] <= { rxd_sr[6:0], rxd_loc };
rxd_fal <= 0;
rxd_ris <= 0;
// if ( rxd_sr[3:0] == 4'b0001 ) begin
// if ( rxd_sr[2:0] == 3'b001 ) begin
// if ( rxd_sr[2:0] == 3'b011 ) begin
if ( rxd_sr[7:6] == 2'b00 && rxd_sr[2:0] == 3'b011 ) begin
rxd_fal <= 1;
end
// if ( rxd_sr[3:0] == 4'b1110 ) begin
// if ( rxd_sr[2:0] == 3'b110 ) begin
// if ( rxd_sr[2:0] == 3'b100 ) begin
if ( rxd_sr[7:6] == 2'b11 && rxd_sr[2:0] == 3'b100 ) begin
rxd_ris <= 1;
end
end
end // proc_din
assign rxd_sample = ~ rxd_sr[2];
//-----------------------------------------------------------------------------
// 16bit Counter used for both rx_start_bit width count and sample count
//-----------------------------------------------------------------------------
always @ ( posedge clk ) begin : proc_cnt
begin
if ( rx_start_a == 1 || rx_start_b == 1 ) begin
rx_cnt_16b <= 16'd0;
end else if ( rx_cnt_en_a_jk == 1 ||
rx_cnt_en_b == 1 ) begin
rx_cnt_16b <= rx_cnt_16b + 1;
end
if ( en_fast_autobaud == 1 ) begin
rx_cnt_16b[15:12] <= 4'd0;
rx_cnt_16b_p1 <= rx_cnt_16b[11];
rx_cnt_16b_roll <= rx_cnt_16b_p1 & ~ rx_cnt_16b[11];
end else begin
rx_cnt_16b_p1 <= rx_cnt_16b[15];
rx_cnt_16b_roll <= rx_cnt_16b_p1 & ~ rx_cnt_16b[15];
end
rx_cnt_16b[15:8] <= 8'd0;// 2016_10_17
end
end // proc_cnt
//-----------------------------------------------------------------------------
// Autobaud off "\n" character. Tricky, as both start bit at D0 are 0.
// When there is no bit_lock, look for falling edge of RXD and count width of
// StartBit and the 1st '0' of 0xA "\n" "01010000".
// bit_lock remains on unless we don't get baud_lock
// bit_lock == Think we know where to sample each symbol.
// baud_lock == We sampled 8 symbols and got 0x0A or "\n"
//-----------------------------------------------------------------------------
always @ ( posedge clk ) begin : proc_s1
begin
sample_first <= 0;
rx_start_a <= 0;
bit_lock_jk_p1 <= bit_lock_jk;
if ( bit_lock_jk == 0 && baud_lock_jk == 0 ) begin
if ( rxd_fal == 1 ) begin
rx_start_a <= 1;
rx_cnt_en_a_jk <= 1;
end else if ( rxd_ris == 1 && rx_cnt_en_a_jk == 1 ) begin
baud_rate_loc <= { 1'b0, rx_cnt_16b[15:1] } - 16'd1;// Div2 Shift
bit_lock_jk <= 1;
rx_start_a <= 1;
rx_cnt_en_a_jk <= 0;
sample_first <= 1;
end
end else begin
rx_cnt_en_a_jk <= 0;
end
if ( clr_bit_lock == 1 ) begin
bit_lock_jk <= 0;
end
if ( en_fast_autobaud == 1 ) begin
baud_rate_loc[15:12] <= 4'd0;
end
//if ( en_autobaud == 0 ) begin
// baud_rate_loc <= 16'd43;
// bit_lock_jk <= 1;
//end
// fix baud rate for smaller design. Note the subtract-2
//baud_rate_loc[15:0] <= 16'd109;// 100 MHz / 921600 baud
//baud_rate_loc[15:0] <= 16'd104;// 96 MHz / 921600 baud = WORKS
//baud_rate_loc[15:0] <= 16'd104;// 12 MHz / 115200 baud = WORKS
baud_rate_loc[15:0] <= 16'd24;// 24 MHz / 921600 baud = WORKS
//baud_rate_loc[15:0] <= 16'd12;// 12 MHz / 921600 baud = DOESNT WORK
//bit_lock_jk <= 1;
end
end // proc_s1
assign flush = rx_ascii_cr;
//-----------------------------------------------------------------------------
// Decode the character bytes
//-----------------------------------------------------------------------------
always @ ( posedge clk ) begin : proc_s3
begin
rx_ascii_cr <= 0;
rx_bad_symbol <= 0;
clr_bit_lock <= 0;
rx_rdy_loc <= 0;
rx_rdy <= rx_rdy_loc & baud_lock_jk;
// Assert baud_lock if 1st char is "\n" else clear and start over.
if ( sample_now_p1 == 1 && rx_bit_cnt == 4'd9 ) begin
if ( rx_byte_loc[7:0] == 8'h0a ) begin
baud_lock_jk <= 1;
rx_ascii_cr <= 1;
clr_jk <= 0;
bad_jk <= 0;
end else if ( baud_lock_jk == 0 ) begin
clr_bit_lock <= 1;
end
// Only expect ASCII 0x20 - 0x7F, detect 0x80+ as bad and clear baud lock
if ( rx_byte_loc[7] == 1 ) begin
rx_bad_symbol <= 1;
end
// Only expect ASCII 0x20 - 0x7F, detect under 0x20 and clear baud lock
if ( rx_byte_loc != 8'h0A && rx_byte_loc < 8'h20 ) begin
rx_bad_symbol <= 1;
end
end
// Grab received byte after last bit received
if ( sample_now_p1 == 1 && rx_bit_cnt == 4'd9 ) begin
rx_rdy_loc <= 1;
end
if ( reset == 1 || rx_bad_symbol == 1 || clr_baudlock == 1 ) begin
baud_lock_jk <= 0;
clr_bit_lock <= 1;
rx_rdy_loc <= 0;
end
// if ( dbg_sample_togl != dbg_sample_togl_p1 ) begin
// clr_jk <= 0;
// bad_jk <= 0;
// end
if ( rx_bad_symbol == 1 ) begin
bad_jk <= 1;
end
if ( clr_baudlock == 1 ) begin
clr_jk <= 1;
end
if ( reset == 1 ) begin
clr_jk <= 0;
bad_jk <= 0;
end
end
end // proc_s3
assign rx_byte_loc = rx_byte_sr[8:1];
assign rx_byte = rx_byte_sr[8:1];
//-----------------------------------------------------------------------------
// look for falling edge of rx_start_bit and count 1/2 way into bit and sample
//-----------------------------------------------------------------------------
always @ ( posedge clk ) begin : proc_s2
begin
tx_en_loc <= 0;
sample_now <= 0;
rx_start_b <= 0;
rx_cnt_en_b <= 0;
rx_byte_jk_p1 <= rx_byte_jk;
sample_now_p1 <= sample_now;
dbg_sample_togl_p1 <= dbg_sample_togl;
if ( rx_byte_jk == 0 ) begin
rx_bit_cnt <= 4'd0;
if ( rxd_fal == 1 ) begin
rx_start_b <= 1;
rx_byte_jk <= 1;// Starting a new Byte
dbg_byte_togl <= ~dbg_byte_togl;
end
end
// When to start sample_now is tricky as for bit_lock, it is 1/2 baud into
// the D1 data bit of \n, after lock, it is 1/2 baud into D0 data bit.
// cnt=0 is StartBit, cnt=1 is D0, cnt=2 is D1
if ( bit_lock_jk == 1 && rx_byte_jk == 1 ) begin
rx_cnt_en_b <= 1;
if (
( baud_lock_jk == 0 && rx_bit_cnt == 4'd2 ) ||
( baud_lock_jk == 1 && rx_bit_cnt == 4'd1 ) ) begin
// Div-2 baud count to sample middle of eye
if ( rx_cnt_16b[15:0] == { 1'b0, baud_rate_loc[15:1] } ) begin
rx_bit_cnt <= rx_bit_cnt + 1;
rx_start_b <= 1;
sample_now <= 1;
end
end else if ( rx_cnt_16b[15:0] == baud_rate_loc[15:0] ) begin
rx_bit_cnt <= rx_bit_cnt + 1;
rx_start_b <= 1;
sample_now <= 1;
end
end
// Assume "\n" and stuff the 1st 0 since it already flew by
if ( sample_first == 1 ) begin
rx_bit_cnt <= 4'd2;
rx_byte_sr[8] <= 0;
rx_byte_sr[7:0] <= rx_byte_sr[8:1];
end
if ( sample_now == 1 ) begin
rx_byte_sr[8] <= rxd_sample;
rx_byte_sr[7:0] <= rx_byte_sr[8:1];
dbg_sample_togl <= ~dbg_sample_togl;
end
if ( sample_now_p1 == 1 && rx_bit_cnt == 4'd9 ) begin
rx_byte_jk <= 0;
rx_bit_cnt <= 4'd0;
end
if ( rx_rdy_loc == 1 && baud_lock_jk == 1 && en_loopback == 1 ) begin
tx_byte_loc <= rx_byte_loc[7:0];
tx_en_loc <= 1;
end else if ( tx_en == 1 ) begin
tx_byte_loc <= tx_byte[7:0];
tx_en_loc <= 1;
end
if ( reset == 1 ) begin
rx_byte_jk <= 0;
rx_byte_sr[8:0] <= 9'd0;
dbg_sample_togl <= 0;
dbg_byte_togl <= 0;
end
end
end // proc_s2
//-----------------------------------------------------------------------------
// TX : Load 8bits into 10bit SR and shift out at the RX baud rate
//-----------------------------------------------------------------------------
always @ ( posedge clk ) begin : proc_tx
begin
tx_shift <= 0;
tx_busy <= 0;
//txd <= txd_loc;
txd <= tx_sr[0];
txd_loc <= tx_sr[0];
//txd_loc <= tx_sr[0] | ~ baud_lock;
tx_now <= 0;
// Load a new Byte to send
if ( tx_en_loc == 1 ) begin
tx_bit_cnt <= 4'd10;
tx_cnt_16b <= 16'h0000;
tx_idle <= 0;
tx_busy <= 1;
tx_shift <= 1;
tx_sr[9:0] <= { 1'b1, tx_byte_loc[7:0], 1'b0 };
// Shift and send the byte until bit_cnt goes to 0
end else if ( tx_bit_cnt != 4'd0 ) begin
tx_cnt_16b <= tx_cnt_16b + 1;
tx_busy <= 1;
tx_idle <= 0;
if ( tx_now == 1 ) begin
tx_shift <= 1;
tx_bit_cnt <= tx_bit_cnt[3:0] - 1;
tx_cnt_16b <= 16'h0000;
tx_sr[9:0] <= { 1'b1, tx_sr[9:1] };
end
end else begin
tx_sr <= 10'H3FF;
if ( tx_now == 1 ) begin
tx_idle <= 1;
end else begin
tx_cnt_16b <= tx_cnt_16b + 1;
end
end
if ( tx_cnt_16b[15:0] == baud_rate_loc[15:0] ) begin
tx_now <= 1;
end
if ( reset == 1 ) begin
tx_bit_cnt <= 4'd0;
tx_idle <= 1;
end
if ( en_fast_autobaud == 1 ) begin
tx_cnt_16b[15:12] <= 4'd0;
end
end
end // proc_tx
endmodule // mesa_uart