Forum Discussion
I'll provide you with synthesizable UART transmitter and receiver Verilog code suitable for Altera MAX II CPLD implementation. But you will need to write your testbench to test out the code:
## UART Transmitter
```verilog
module uart_tx #(
parameter CLOCK_FREQ = 50000000, // 50 MHz clock
parameter BAUD_RATE = 9600 // 9600 baud
)(
input clk,
input rst_n,
input [7:0] tx_data,
input tx_start,
output reg tx,
output reg tx_busy
);
localparam BAUD_TICKS = CLOCK_FREQ / BAUD_RATE;
localparam COUNTER_WIDTH = $clog2(BAUD_TICKS);
reg [COUNTER_WIDTH-1:0] baud_counter;
reg [3:0] bit_counter;
reg [9:0] tx_shift_reg; // Start bit + 8 data bits + Stop bit
reg baud_tick;
// Baud rate generator
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
baud_counter <= 0;
baud_tick <= 1'b0;
end else begin
if (baud_counter == BAUD_TICKS - 1) begin
baud_counter <= 0;
baud_tick <= 1'b1;
end else begin
baud_counter <= baud_counter + 1;
baud_tick <= 1'b0;
end
end
end
// UART transmitter state machine
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
tx <= 1'b1; // Idle high
tx_busy <= 1'b0;
bit_counter <= 0;
tx_shift_reg <= 10'h3FF; // All ones (idle)
end else begin
if (tx_start && !tx_busy) begin
tx_shift_reg <= {1'b1, tx_data, 1'b0}; // Stop + Data + Start
tx_busy <= 1'b1;
bit_counter <= 0;
end else if (tx_busy && baud_tick) begin
tx <= tx_shift_reg[0];
tx_shift_reg <= {1'b1, tx_shift_reg[9:1]};
if (bit_counter == 9) begin
tx_busy <= 1'b0;
bit_counter <= 0;
end else begin
bit_counter <= bit_counter + 1;
end
end
end
end
endmodule
```
## UART Receiver
```verilog
module uart_rx #(
parameter CLOCK_FREQ = 50000000, // 50 MHz clock
parameter BAUD_RATE = 9600 // 9600 baud
)(
input clk,
input rst_n,
input rx,
output reg [7:0] rx_data,
output reg rx_valid
);
localparam BAUD_TICKS = CLOCK_FREQ / BAUD_RATE;
localparam HALF_BAUD_TICKS = BAUD_TICKS / 2;
localparam COUNTER_WIDTH = $clog2(BAUD_TICKS);
reg [COUNTER_WIDTH-1:0] baud_counter;
reg [3:0] bit_counter;
reg [7:0] rx_shift_reg;
reg baud_tick;
reg rx_sync1, rx_sync2; // Synchronizer for rx input
// Input synchronizer
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rx_sync1 <= 1'b1;
rx_sync2 <= 1'b1;
end else begin
rx_sync1 <= rx;
rx_sync2 <= rx_sync1;
end
end
// State machine states
localparam IDLE = 2'b00;
localparam START = 2'b01;
localparam DATA = 2'b10;
localparam STOP = 2'b11;
reg [1:0] state;
// Baud rate counter
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
baud_counter <= 0;
baud_tick <= 1'b0;
end else begin
if (state == IDLE) begin
baud_counter <= 0;
baud_tick <= 1'b0;
end else if (baud_counter == BAUD_TICKS - 1) begin
baud_counter <= 0;
baud_tick <= 1'b1;
end else begin
baud_counter <= baud_counter + 1;
baud_tick <= 1'b0;
end
end
end
// UART receiver state machine
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
bit_counter <= 0;
rx_shift_reg <= 0;
rx_data <= 0;
rx_valid <= 1'b0;
end else begin
rx_valid <= 1'b0; // Default: no valid data
case (state)
IDLE: begin
if (!rx_sync2) begin // Start bit detected
state <= START;
baud_counter <= 0;
end
end
START: begin
if (baud_counter == HALF_BAUD_TICKS) begin
if (!rx_sync2) begin // Confirm start bit
state <= DATA;
bit_counter <= 0;
baud_counter <= 0;
end else begin
state <= IDLE; // False start bit
end
end
end
DATA: begin
if (baud_tick) begin
rx_shift_reg <= {rx_sync2, rx_shift_reg[7:1]};
if (bit_counter == 7) begin
state <= STOP;
bit_counter <= 0;
end else begin
bit_counter <= bit_counter + 1;
end
end
end
STOP: begin
if (baud_tick) begin
if (rx_sync2) begin // Valid stop bit
rx_data <= rx_shift_reg;
rx_valid <= 1'b1;
end
state <= IDLE;
end
end
endcase
end
end
endmodule
```
## Top-Level UART Module
```verilog
module uart_top #(
parameter CLOCK_FREQ = 50000000,
parameter BAUD_RATE = 9600
)(
input clk,
input rst_n,
// TX interface
input [7:0] tx_data,
input tx_start,
output tx_busy,
output uart_tx,
// RX interface
input uart_rx,
output [7:0] rx_data,
output rx_valid
);
uart_tx #(
.CLOCK_FREQ(CLOCK_FREQ),
.BAUD_RATE(BAUD_RATE)
) tx_inst (
.clk(clk),
.rst_n(rst_n),
.tx_data(tx_data),
.tx_start(tx_start),
.tx(uart_tx),
.tx_busy(tx_busy)
);
uart_rx #(
.CLOCK_FREQ(CLOCK_FREQ),
.BAUD_RATE(BAUD_RATE)
) rx_inst (
.clk(clk),
.rst_n(rst_n),
.rx(uart_rx),
.rx_data(rx_data),
.rx_valid(rx_valid)
);
endmodule
```
## Key Features
- **Parameterizable**: Clock frequency and baud rate can be configured
- **Synchronous Design**: Uses proper clock domain design suitable for MAX II
- **Input Synchronization**: RX input is properly synchronized to avoid metastability
- **Standard UART Protocol**: 1 start bit, 8 data bits, 1 stop bit, no parity
- **Handshaking**: TX has busy signal, RX has valid signal
## Usage Example
```verilog
// Instantiate in your top-level design
uart_top #(
.CLOCK_FREQ(50000000), // 50 MHz
.BAUD_RATE(115200) // 115200 baud
) uart_inst (
.clk(clk_50mhz),
.rst_n(reset_n),
.tx_data(data_to_send),
.tx_start(send_enable),
.tx_busy(uart_busy),
.uart_tx(uart_tx_pin),
.uart_rx(uart_rx_pin),
.rx_data(received_data),
.rx_valid(data_ready)
);
```