forked from SJTU-YONGFU-RESEARCH-GRP/core
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparameterized_uart_rx.v
More file actions
executable file
·286 lines (264 loc) · 11.9 KB
/
parameterized_uart_rx.v
File metadata and controls
executable file
·286 lines (264 loc) · 11.9 KB
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
/**
* Parameterized UART Receiver
*
* This module implements a configurable Universal Asynchronous Receiver/Transmitter
* (UART) receiver that can be configured for different data widths, baud rates,
* parity options, and stop bit counts.
*
* Key Features:
* - Configurable data width: 5-9 bits (standard UART supports 5-9 bits)
* - Programmable baud rate: Any baud rate via CLK_FREQ and BAUD_RATE parameters
* - Optional parity: Even or odd parity support with error detection
* - Configurable stop bits: 1 or 2 stop bits
* - Error detection: Parity error and frame error flags
* - Input synchronization: 2-flop synchronizer prevents metastability
*
* UART Frame Format:
* - Start bit: Always 0 (1 bit)
* - Data bits: DATA_WIDTH bits (LSB first)
* - Parity bit: Optional (1 bit if PARITY_EN = 1)
* - Stop bit(s): 1 or STOP_BITS (always 1)
*
* Baud Rate Calculation:
* - CLKS_PER_BIT = CLK_FREQ / BAUD_RATE
* - CLKS_PER_HALF_BIT = CLKS_PER_BIT / 2 (for start bit sampling)
* - Timer counts CLKS_PER_BIT cycles per bit
*
* State Machine:
* - IDLE: Waiting for start bit (falling edge on rx)
* - START_BIT: Sampling start bit in middle
* - DATA_BITS: Receiving data bits (LSB first)
* - PARITY_BIT: Receiving parity bit (if enabled)
* - STOP_BIT: Receiving stop bit(s)
* - CLEANUP: Waiting before returning to IDLE
*
* Error Detection:
* - Parity error: Received parity doesn't match calculated parity
* - Frame error: Stop bit is not high (1)
*
* Use Cases:
* - Serial communication interfaces
* - Debug ports
* - Legacy device interfaces
* - Low-speed data links
*
* @param CLK_FREQ Clock frequency in Hz (default: 50000 Hz for simulation)
* @param BAUD_RATE Baud rate in bits per second (default: 1000 bps for simulation)
* @param DATA_WIDTH Data width: 5-9 bits (default: 8 bits)
* @param PARITY_EN Parity enable: 0 = disabled, 1 = enabled (default: 0)
* @param PARITY_TYPE Parity type: 0 = even, 1 = odd (default: 0, only if PARITY_EN=1)
* @param STOP_BITS Number of stop bits: 1 or 2 (default: 1)
*
* @input clk System clock
* @input rst_n Active-low reset
* @input rx UART RX line (serial input)
* @output data_out[DATA_WIDTH-1:0] Received data
* @output data_valid Data valid pulse (high for one cycle when data ready)
* @output parity_error Parity error flag (high if parity mismatch)
* @output frame_error Frame error flag (high if stop bit invalid)
*/
module parameterized_uart_rx #(
parameter CLK_FREQ = 50000, // Clock frequency in Hz (reduced for simulation)
parameter BAUD_RATE = 1000, // Baud rate in bits per second (reduced for simulation)
parameter DATA_WIDTH = 8, // Data width (5-9 bits)
parameter PARITY_EN = 0, // Parity enable (0=disabled, 1=enabled)
parameter PARITY_TYPE = 0, // Parity type (0=even, 1=odd)
parameter STOP_BITS = 1 // Number of stop bits (1 or 2)
) (
input wire clk, // System clock
input wire rst_n, // Active low reset
input wire rx, // UART RX line
output reg [DATA_WIDTH-1:0] data_out, // Received data
output reg data_valid, // Data valid pulse
output reg parity_error, // Parity error flag
output reg frame_error // Frame error flag
);
// Derived parameters
localparam [31:0] CLKS_PER_BIT = CLK_FREQ / BAUD_RATE;
localparam [31:0] CLKS_PER_HALF_BIT = CLKS_PER_BIT / 2;
// State definitions
localparam IDLE = 3'd0;
localparam START_BIT = 3'd1;
localparam DATA_BITS = 3'd2;
localparam PARITY_BIT = 3'd3;
localparam STOP_BIT = 3'd4;
localparam CLEANUP = 3'd5;
// Registers
reg [2:0] state;
reg [31:0] clk_counter;
reg [$clog2(DATA_WIDTH)-1:0] bit_index;
reg [DATA_WIDTH-1:0] rx_data;
reg rx_parity;
reg rx_sync, rx_meta; // 2FF synchronizer
// Debug signals
// synthesis translate_off
initial begin
$display("UART RX Parameters: CLK_FREQ=%0d, BAUD_RATE=%0d, CLKS_PER_BIT=%0d",
CLK_FREQ, BAUD_RATE, CLKS_PER_BIT);
end
// synthesis translate_on
// ============================================================================
// Input Synchronization
// ============================================================================
// Synchronize the RX input using 2-flop synchronizer to prevent metastability
// This is critical because RX is asynchronous to the system clock
// UART idle state is high (1), so reset to 1
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
// Reset: Set to idle state (high)
rx_meta <= 1'b1;
rx_sync <= 1'b1;
end else begin
// Two-stage synchronizer: reduces metastability probability
rx_meta <= rx; // First stage: may be metastable
rx_sync <= rx_meta; // Second stage: much more stable
end
end
// Main UART RX state machine
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
clk_counter <= 0;
bit_index <= 0;
rx_data <= 0;
rx_parity <= 0;
data_out <= 0;
data_valid <= 0;
parity_error <= 0;
frame_error <= 0;
end else begin
// Default assignment
data_valid <= 0;
case (state)
IDLE: begin
// Reset counters
clk_counter <= 0;
bit_index <= 0;
parity_error <= 0;
frame_error <= 0;
// Wait for start bit (low signal)
if (rx_sync == 1'b0) begin
// synthesis translate_off
$display("UART RX: Start bit detected");
// synthesis translate_on
state <= START_BIT;
end
end
START_BIT: begin
// Sample in the middle of the start bit
if (clk_counter == CLKS_PER_HALF_BIT) begin
// Verify it's still low
if (rx_sync == 1'b0) begin
// synthesis translate_off
$display("UART RX: Valid start bit confirmed");
// synthesis translate_on
clk_counter <= 0;
state <= DATA_BITS;
bit_index <= 0;
rx_parity <= PARITY_TYPE; // Initialize parity
end else begin
// False start bit
// synthesis translate_off
$display("UART RX: False start bit detected");
// synthesis translate_on
state <= IDLE;
end
end else begin
clk_counter <= clk_counter + 1;
end
end
DATA_BITS: begin
// Sample in the middle of each data bit
if (clk_counter == CLKS_PER_BIT) begin
clk_counter <= 0;
// synthesis translate_off
$display("UART RX: Received data bit %0d = %0b", bit_index, rx_sync);
// synthesis translate_on
rx_data[bit_index] <= rx_sync;
// Update parity calculation
if (PARITY_EN) begin
rx_parity <= rx_parity ^ rx_sync;
end
// Use a zero-extended version of bit_index for comparison
if ({1'b0, bit_index} < (DATA_WIDTH - 1)) begin
bit_index <= bit_index + 1;
end else begin
bit_index <= 0;
state <= PARITY_EN ? PARITY_BIT : STOP_BIT;
// synthesis translate_off
if (!PARITY_EN) $display("UART RX: All data bits received, moving to stop bit");
else $display("UART RX: All data bits received, moving to parity bit");
// synthesis translate_on
end
end else begin
clk_counter <= clk_counter + 1;
end
end
PARITY_BIT: begin
// Sample in the middle of the parity bit
if (clk_counter == CLKS_PER_BIT) begin
// synthesis translate_off
$display("UART RX: Received parity bit = %0b, calculated parity = %0b", rx_sync, rx_parity);
// synthesis translate_on
clk_counter <= 0;
state <= STOP_BIT;
// Check parity
if (rx_sync != rx_parity) begin
parity_error <= 1'b1;
// synthesis translate_off
$display("UART RX: Parity error detected!");
// synthesis translate_on
end
end else begin
clk_counter <= clk_counter + 1;
end
end
STOP_BIT: begin
// Sample in the middle of the stop bit(s)
if (clk_counter == CLKS_PER_BIT) begin
clk_counter <= 0;
// Check for frame error (stop bit should be high)
if (rx_sync != 1'b1) begin
frame_error <= 1'b1;
// synthesis translate_off
$display("UART RX: Frame error detected! Stop bit is %0b", rx_sync);
// synthesis translate_on
end
if (STOP_BITS > 1 && bit_index == 0) begin
// For multiple stop bits
bit_index <= 1;
// synthesis translate_off
$display("UART RX: First stop bit received, waiting for second stop bit");
// synthesis translate_on
end else begin
state <= CLEANUP;
// Output the received data regardless of stop bit value
data_out <= rx_data;
data_valid <= 1'b1;
// synthesis translate_off
$display("UART RX: Successfully received byte 0x%h", rx_data);
// synthesis translate_on
end
end else begin
clk_counter <= clk_counter + 1;
end
end
CLEANUP: begin
// Wait one bit time to ensure proper spacing between bytes
if (clk_counter == CLKS_PER_BIT / 4) begin // Reduced to 1/4 bit time
state <= IDLE;
clk_counter <= 0;
// synthesis translate_off
$display("UART RX: Returning to IDLE state");
// synthesis translate_on
end else begin
clk_counter <= clk_counter + 1;
end
end
default: begin
state <= IDLE;
end
endcase
end
end
endmodule