]>
Commit | Line | Data |
---|---|---|
1 | `timescale 1ns / 1ps | |
2 | // Documented Verilog UART | |
3 | // Copyright (C) 2010 Timothy Goddard (tim@goddard.net.nz) | |
4 | // Distributed under the MIT licence. | |
5 | // | |
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | // of this software and associated documentation files (the "Software"), to deal | |
8 | // in the Software without restriction, including without limitation the rights | |
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | // copies of the Software, and to permit persons to whom the Software is | |
11 | // furnished to do so, subject to the following conditions: | |
12 | // | |
13 | // The above copyright notice and this permission notice shall be included in | |
14 | // all copies or substantial portions of the Software. | |
15 | // | |
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | // THE SOFTWARE. | |
23 | // | |
24 | module uart( | |
25 | input clk, // The master clock for this module | |
26 | input rst, // Synchronous reset. | |
27 | input rx, // Incoming serial line | |
28 | output tx, // Outgoing serial line | |
29 | input transmit, // Signal to transmit | |
30 | input [7:0] tx_byte, // Byte to transmit | |
31 | output received, // Indicated that a byte has been received. | |
32 | output [7:0] rx_byte, // Byte received | |
33 | output is_receiving, // Low when receive line is idle. | |
34 | output is_transmitting, // Low when transmit line is idle. | |
35 | output recv_error // Indicates error in receiving packet. | |
36 | ); | |
37 | ||
38 | parameter CLOCK_DIVIDE = 1302; // clock rate (50Mhz) / (baud rate (9600) * 4) | |
39 | ||
40 | // States for the receiving state machine. | |
41 | // These are just constants, not parameters to override. | |
42 | parameter RX_IDLE = 0; | |
43 | parameter RX_CHECK_START = 1; | |
44 | parameter RX_READ_BITS = 2; | |
45 | parameter RX_CHECK_STOP = 3; | |
46 | parameter RX_DELAY_RESTART = 4; | |
47 | parameter RX_ERROR = 5; | |
48 | parameter RX_RECEIVED = 6; | |
49 | ||
50 | // States for the transmitting state machine. | |
51 | // Constants - do not override. | |
52 | parameter TX_IDLE = 0; | |
53 | parameter TX_SENDING = 1; | |
54 | parameter TX_DELAY_RESTART = 2; | |
55 | ||
56 | reg [10:0] rx_clk_divider = CLOCK_DIVIDE; | |
57 | reg [10:0] tx_clk_divider = CLOCK_DIVIDE; | |
58 | ||
59 | reg [2:0] recv_state = RX_IDLE; | |
60 | reg [5:0] rx_countdown; | |
61 | reg [3:0] rx_bits_remaining; | |
62 | reg [7:0] rx_data; | |
63 | ||
64 | reg tx_out = 1'b1; | |
65 | reg [1:0] tx_state = TX_IDLE; | |
66 | reg [5:0] tx_countdown; | |
67 | reg [3:0] tx_bits_remaining; | |
68 | reg [7:0] tx_data; | |
69 | ||
70 | assign received = recv_state == RX_RECEIVED; | |
71 | assign recv_error = recv_state == RX_ERROR; | |
72 | assign is_receiving = recv_state != RX_IDLE; | |
73 | assign rx_byte = rx_data; | |
74 | ||
75 | assign tx = tx_out; | |
76 | assign is_transmitting = tx_state != TX_IDLE; | |
77 | ||
78 | always @(posedge clk) begin | |
79 | if (rst) begin | |
80 | recv_state = RX_IDLE; | |
81 | tx_state = TX_IDLE; | |
82 | end | |
83 | ||
84 | // The clk_divider counter counts down from | |
85 | // the CLOCK_DIVIDE constant. Whenever it | |
86 | // reaches 0, 1/16 of the bit period has elapsed. | |
87 | // Countdown timers for the receiving and transmitting | |
88 | // state machines are decremented. | |
89 | rx_clk_divider = rx_clk_divider - 1; | |
90 | if (!rx_clk_divider) begin | |
91 | rx_clk_divider = CLOCK_DIVIDE; | |
92 | rx_countdown = rx_countdown - 1; | |
93 | end | |
94 | tx_clk_divider = tx_clk_divider - 1; | |
95 | if (!tx_clk_divider) begin | |
96 | tx_clk_divider = CLOCK_DIVIDE; | |
97 | tx_countdown = tx_countdown - 1; | |
98 | end | |
99 | ||
100 | // Receive state machine | |
101 | case (recv_state) | |
102 | RX_IDLE: begin | |
103 | // A low pulse on the receive line indicates the | |
104 | // start of data. | |
105 | if (!rx) begin | |
106 | // Wait half the period - should resume in the | |
107 | // middle of this first pulse. | |
108 | rx_clk_divider = CLOCK_DIVIDE; | |
109 | rx_countdown = 2; | |
110 | recv_state = RX_CHECK_START; | |
111 | end | |
112 | end | |
113 | RX_CHECK_START: begin | |
114 | if (!rx_countdown) begin | |
115 | // Check the pulse is still there | |
116 | if (!rx) begin | |
117 | // Pulse still there - good | |
118 | // Wait the bit period to resume half-way | |
119 | // through the first bit. | |
120 | rx_countdown = 4; | |
121 | rx_bits_remaining = 8; | |
122 | recv_state = RX_READ_BITS; | |
123 | end else begin | |
124 | // Pulse lasted less than half the period - | |
125 | // not a valid transmission. | |
126 | recv_state = RX_ERROR; | |
127 | end | |
128 | end | |
129 | end | |
130 | RX_READ_BITS: begin | |
131 | if (!rx_countdown) begin | |
132 | // Should be half-way through a bit pulse here. | |
133 | // Read this bit in, wait for the next if we | |
134 | // have more to get. | |
135 | rx_data = {rx, rx_data[7:1]}; | |
136 | rx_countdown = 4; | |
137 | rx_bits_remaining = rx_bits_remaining - 1; | |
138 | recv_state = rx_bits_remaining ? RX_READ_BITS : RX_CHECK_STOP; | |
139 | end | |
140 | end | |
141 | RX_CHECK_STOP: begin | |
142 | if (!rx_countdown) begin | |
143 | // Should resume half-way through the stop bit | |
144 | // This should be high - if not, reject the | |
145 | // transmission and signal an error. | |
146 | recv_state = rx ? RX_RECEIVED : RX_ERROR; | |
147 | end | |
148 | end | |
149 | RX_DELAY_RESTART: begin | |
150 | // Waits a set number of cycles before accepting | |
151 | // another transmission. | |
152 | recv_state = rx_countdown ? RX_DELAY_RESTART : RX_IDLE; | |
153 | end | |
154 | RX_ERROR: begin | |
155 | // There was an error receiving. | |
156 | // Raises the recv_error flag for one clock | |
157 | // cycle while in this state and then waits | |
158 | // 2 bit periods before accepting another | |
159 | // transmission. | |
160 | rx_countdown = 8; | |
161 | recv_state = RX_DELAY_RESTART; | |
162 | end | |
163 | RX_RECEIVED: begin | |
164 | // Successfully received a byte. | |
165 | // Raises the received flag for one clock | |
166 | // cycle while in this state. | |
167 | recv_state = RX_IDLE; | |
168 | end | |
169 | endcase | |
170 | ||
171 | // Transmit state machine | |
172 | case (tx_state) | |
173 | TX_IDLE: begin | |
174 | if (transmit) begin | |
175 | // If the transmit flag is raised in the idle | |
176 | // state, start transmitting the current content | |
177 | // of the tx_byte input. | |
178 | tx_data = tx_byte; | |
179 | // Send the initial, low pulse of 1 bit period | |
180 | // to signal the start, followed by the data | |
181 | tx_clk_divider = CLOCK_DIVIDE; | |
182 | tx_countdown = 4; | |
183 | tx_out = 0; | |
184 | tx_bits_remaining = 8; | |
185 | tx_state = TX_SENDING; | |
186 | end | |
187 | end | |
188 | TX_SENDING: begin | |
189 | if (!tx_countdown) begin | |
190 | if (tx_bits_remaining) begin | |
191 | tx_bits_remaining = tx_bits_remaining - 1; | |
192 | tx_out = tx_data[0]; | |
193 | tx_data = {1'b0, tx_data[7:1]}; | |
194 | tx_countdown = 4; | |
195 | tx_state = TX_SENDING; | |
196 | end else begin | |
197 | // Set delay to send out 2 stop bits. | |
198 | tx_out = 1; | |
199 | tx_countdown = 8; | |
200 | tx_state = TX_DELAY_RESTART; | |
201 | end | |
202 | end | |
203 | end | |
204 | TX_DELAY_RESTART: begin | |
205 | // Wait until tx_countdown reaches the end before | |
206 | // we send another transmission. This covers the | |
207 | // "stop bit" delay. | |
208 | tx_state = tx_countdown ? TX_DELAY_RESTART : TX_IDLE; | |
209 | end | |
210 | endcase | |
211 | end | |
212 | ||
213 | endmodule |