Skip to content

Commit f3075c2

Browse files
committed
Added SV version of UART receiver
1 parent 536a8b8 commit f3075c2

File tree

2 files changed

+127
-3
lines changed

2 files changed

+127
-3
lines changed

uart_rx.sv

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
//--------------------------------------------------------------------------------
2+
// uart_rx.sv
3+
// Konstantin Pavlov, pavlovconst@gmail.com
4+
//--------------------------------------------------------------------------------
5+
6+
// INFO --------------------------------------------------------------------------------
7+
// Straightforward yet simple UART receiver implementation
8+
// for FPGA written in Verilog
9+
10+
// Expects at least one stop bit
11+
// Features continuous data aquisition at BAUD levels up to CLK_HZ / 2
12+
// Features early asynchronous 'busy' reset
13+
14+
15+
/* --- INSTANTIATION TEMPLATE BEGIN ---
16+
17+
uart_rx #(
18+
.CLK_HZ( 200_000_000 ), // in Hertz
19+
.BAUD( 9600 ) // max. BAUD is CLK_HZ / 2
20+
)(
21+
.clk( ),
22+
.nrst( ),
23+
24+
.rx_data( ),
25+
.rx_done( ),
26+
.rxd( )
27+
);
28+
29+
--- INSTANTIATION TEMPLATE END ---*/
30+
31+
32+
module uart_rx #( parameter
33+
CLK_HZ = 200_000_000,
34+
BAUD = 9600,
35+
bit [15:0] BAUD_DIVISOR_2 = CLK_HZ / BAUD / 2
36+
)(
37+
input clk,
38+
input nrst,
39+
40+
output logic [7:0] rx_data = '0,
41+
output logic rx_busy = 1'b0,
42+
output logic rx_done, // read strobe
43+
output logic rx_err,
44+
input rxd
45+
);
46+
47+
48+
// synchronizing external rxd pin to avoid metastability
49+
logic rxd_s;
50+
delay #(
51+
.LENGTH( 2 ),
52+
.WIDTH( 1 )
53+
) rxd_synch (
54+
.clk( clk ),
55+
.nrst( nrst ),
56+
.ena( 1'b1 ),
57+
.in( rxd ),
58+
.out( rxd_s )
59+
);
60+
61+
62+
logic start_bit_strobe;
63+
edge_detect rxd_fall_detector (
64+
.clk( clk ),
65+
.nrst( nrst ),
66+
.in( rxd_s ),
67+
.falling( start_bit_strobe )
68+
);
69+
70+
71+
logic [15:0] rx_sample_cntr = (BAUD_DIVISOR_2 - 1'b1);
72+
73+
logic rx_do_sample;
74+
assign rx_do_sample = (rx_sample_cntr[15:0] == '0);
75+
76+
77+
// {rx_data[7:0],rx_data_9th_bit} is actually a shift register
78+
logic rx_data_9th_bit = 1'b0;
79+
always_ff @ (posedge clk) begin
80+
if( ~nrst ) begin
81+
rx_busy <= 1'b0;
82+
rx_sample_cntr <= (BAUD_DIVISOR_2 - 1'b1);
83+
{rx_data[7:0],rx_data_9th_bit} <= '0;
84+
end else begin
85+
if( ~rx_busy ) begin
86+
if( start_bit_strobe ) begin
87+
88+
// wait for 1,5-bit period till next sample
89+
rx_sample_cntr[15:0] <= (BAUD_DIVISOR_2 * 3 - 1'b1);
90+
rx_busy <= 1'b1;
91+
{rx_data[7:0],rx_data_9th_bit} <= 9'b100000000;
92+
93+
end
94+
end else begin
95+
96+
if( rx_sample_cntr[15:0] == '0 ) begin
97+
// wait for 1-bit-period till next sample
98+
rx_sample_cntr[15:0] <= (BAUD_DIVISOR_2 * 2 - 1'b1);
99+
end else begin
100+
// counting and sampling only when 'busy'
101+
rx_sample_cntr[15:0] <= rx_sample_cntr[15:0] - 1'b1;
102+
end
103+
104+
if( rx_do_sample ) begin
105+
if( rx_data_9th_bit == 1'b1 ) begin
106+
// early asynchronous 'busy' reset
107+
rx_busy <= 1'b0;
108+
end else begin
109+
{rx_data[7:0],rx_data_9th_bit} <= {rxd_s, rx_data[7:0]};
110+
end
111+
end
112+
113+
end // ~rx_busy
114+
end // ~nrst
115+
end
116+
117+
always_comb begin
118+
// rx_done and rx_busy fall simultaneously
119+
rx_done <= rx_data_9th_bit && rx_do_sample && rxd_s;
120+
rx_err <= rx_data_9th_bit && rx_do_sample && ~rxd_s;
121+
end
122+
123+
endmodule
124+

uart_tx.sv

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ uart_tx #(
3232
--- INSTANTIATION TEMPLATE END ---*/
3333

3434

35-
module uart_tx #(
35+
module uart_tx #( parameter
3636
CLK_HZ = 200_000_000,
3737
BAUD = 9600,
3838
bit [15:0] BAUD_DIVISOR = CLK_HZ / BAUD
@@ -49,7 +49,7 @@ module uart_tx #(
4949

5050
logic [9:0] tx_shifter = '0;
5151
logic [15:0] tx_sample_cntr = '0;
52-
always @ (posedge clk) begin
52+
always_ff @ (posedge clk) begin
5353
if( (~nrst) || (tx_sample_cntr[15:0] == '0) ) begin
5454
tx_sample_cntr[15:0] <= (BAUD_DIVISOR-1'b1);
5555
end else begin
@@ -61,7 +61,7 @@ logic tx_do_sample;
6161
assign tx_do_sample = (tx_sample_cntr[15:0] == '0);
6262

6363

64-
always @ (posedge clk) begin
64+
always_ff @ (posedge clk) begin
6565
if( ~nrst ) begin
6666
tx_busy <= 1'b0;
6767
tx_shifter[9:0] <= '0;

0 commit comments

Comments
 (0)