- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi everyone,
I'm currently working on implementing UART communication on an Altera MAX II CPLD using Verilog. As I'm still learning, I would really appreciate it if anyone could share UART transmitter and receiver Verilog code. Thanks in advance.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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)
);
```
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
compiling the Verilog uart code, I see multiple drivers for baud_counter in uart_rx module. Can be fixed by merging Baud rate counter and UART receiver state machine always blocks.
Regards
Frank
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
Is that code is working for you, I barely see any waveform on the tx line.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
How about try the below:
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;
// Combined baud counter and 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;
baud_counter <= 0;
baud_tick <= 1'b0;
end else begin
rx_valid <= 1'b0; // Default: no valid data
baud_tick <= 1'b0; // Default: no baud tick
case (state)
IDLE: begin
baud_counter <= 0;
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 else begin
baud_counter <= baud_counter + 1;
end
end
DATA: begin
if (baud_counter == BAUD_TICKS - 1) begin
baud_counter <= 0;
baud_tick <= 1'b1;
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 else begin
baud_counter <= baud_counter + 1;
end
end
STOP: begin
if (baud_counter == BAUD_TICKS - 1) begin
baud_counter <= 0;
baud_tick <= 1'b1;
if (rx_sync2) begin // Valid stop bit
rx_data <= rx_shift_reg;
rx_valid <= 1'b1;
end
state <= IDLE;
end else begin
baud_counter <= baud_counter + 1;
end
end
endcase
end
end
endmodule
You're correct! The issue is that both the baud rate counter logic and the state machine are trying to drive the `baud_counter` signal, creating multiple drivers. Here's the fixed UART receiver code with the two always blocks merged:
## Fixed UART Receiver Code
```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;
// Combined baud counter and 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;
baud_counter <= 0;
baud_tick <= 1'b0;
end else begin
rx_valid <= 1'b0; // Default: no valid data
baud_tick <= 1'b0; // Default: no baud tick
case (state)
IDLE: begin
baud_counter <= 0;
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 else begin
baud_counter <= baud_counter + 1;
end
end
DATA: begin
if (baud_counter == BAUD_TICKS - 1) begin
baud_counter <= 0;
baud_tick <= 1'b1;
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 else begin
baud_counter <= baud_counter + 1;
end
end
STOP: begin
if (baud_counter == BAUD_TICKS - 1) begin
baud_counter <= 0;
baud_tick <= 1'b1;
if (rx_sync2) begin // Valid stop bit
rx_data <= rx_shift_reg;
rx_valid <= 1'b1;
end
state <= IDLE;
end else begin
baud_counter <= baud_counter + 1;
end
end
endcase
end
end
endmodule
```
## Key Changes Made
1. **Merged Always Blocks**: Combined the separate baud counter and state machine always blocks into a single always block to eliminate multiple drivers for `baud_counter`.
2. **State-Specific Counter Logic**: The baud counter now resets and increments based on the current state:
- **IDLE**: Counter is reset to 0
- **START**: Counter increments until half baud period
- **DATA/STOP**: Counter increments until full baud period, then resets
3. **Simplified Baud Tick Generation**: The `baud_tick` signal is now generated inline with the counter logic rather than in a separate process.
## Complete Updated Top-Level Module
The UART transmitter and top-level module remain the same. Just replace the `uart_rx` module with the fixed version above.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
See if the below testbench will help you also?
`timescale 1ns/1ps
module tb_uart_top;
// Parameters
parameter CLOCK_FREQ = 50000000; // 50 MHz
parameter BAUD_RATE = 115200; // 115200 baud for faster simulation
parameter CLK_PERIOD = 20.0; // 50 MHz = 20ns period
parameter BIT_PERIOD = 1000000000.0 / BAUD_RATE; // Bit period in ns
// DUT signals
reg clk;
reg rst_n;
// TX interface
reg [7:0] tx_data;
reg tx_start;
wire tx_busy;
wire uart_tx;
// RX interface
reg uart_rx;
wire [7:0] rx_data;
wire rx_valid;
// Test variables
reg [7:0] test_data_queue[$];
reg [7:0] received_data_queue[$];
integer test_count = 0;
integer error_count = 0;
// Clock generation
initial begin
clk = 0;
forever #(CLK_PERIOD/2) clk = ~clk;
end
// DUT instantiation
uart_top #(
.CLOCK_FREQ(CLOCK_FREQ),
.BAUD_RATE(BAUD_RATE)
) dut (
.clk(clk),
.rst_n(rst_n),
.tx_data(tx_data),
.tx_start(tx_start),
.tx_busy(tx_busy),
.uart_tx(uart_tx),
.uart_rx(uart_rx),
.rx_data(rx_data),
.rx_valid(rx_valid)
);
// Task to send a byte via UART TX
task send_byte(input [7:0] data);
begin
@(posedge clk);
while (tx_busy) @(posedge clk); // Wait for TX to be ready
tx_data = data;
tx_start = 1'b1;
@(posedge clk);
tx_start = 1'b0;
test_data_queue.push_back(data);
$display("Time %0t: Sending byte 0x%02h", $time, data);
@(posedge clk);
while (tx_busy) @(posedge clk); // Wait for transmission to complete
end
endtask
// Task to simulate receiving a byte via UART RX
task receive_byte(input [7:0] data);
integer i;
begin
$display("Time %0t: Simulating RX byte 0x%02h", $time, data);
// Send start bit (0)
uart_rx = 1'b0;
#(BIT_PERIOD);
// Send 8 data bits (LSB first)
for (i = 0; i < 8; i = i + 1) begin
uart_rx = data[i];
#(BIT_PERIOD);
end
// Send stop bit (1)
uart_rx = 1'b1;
#(BIT_PERIOD);
end
endtask
// Monitor RX valid data
always @(posedge clk) begin
if (rx_valid) begin
received_data_queue.push_back(rx_data);
$display("Time %0t: Received byte 0x%02h", $time, rx_data);
end
end
// Task to verify received data
task verify_received_data();
reg [7:0] expected, actual;
begin
while (test_data_queue.size() > 0 && received_data_queue.size() > 0) begin
expected = test_data_queue.pop_front();
actual = received_data_queue.pop_front();
if (expected == actual) begin
$display("✓ PASS: Expected 0x%02h, Got 0x%02h", expected, actual);
end else begin
$display("✗ FAIL: Expected 0x%02h, Got 0x%02h", expected, actual);
error_count = error_count + 1;
end
test_count = test_count + 1;
end
end
endtask
// Loopback connection for self-test
always @(*) begin
uart_rx = uart_tx;
end
// Main test sequence
initial begin
// Initialize signals
rst_n = 1'b0;
tx_data = 8'h00;
tx_start = 1'b0;
uart_rx = 1'b1; // UART idle state
$display("=== UART Testbench Starting ===");
$display("Clock Frequency: %0d Hz", CLOCK_FREQ);
$display("Baud Rate: %0d", BAUD_RATE);
$display("Bit Period: %0.2f ns", BIT_PERIOD);
// Reset sequence
#(CLK_PERIOD * 10);
rst_n = 1'b1;
#(CLK_PERIOD * 10);
// Test 1: Basic loopback test
$display("\n=== Test 1: Basic Loopback Test ===");
send_byte(8'h55); // Alternating pattern
send_byte(8'hAA); // Inverse alternating pattern
send_byte(8'h00); // All zeros
send_byte(8'hFF); // All ones
// Wait for data to be received
#(BIT_PERIOD * 20);
verify_received_data();
// Test 2: ASCII characters
$display("\n=== Test 2: ASCII Character Test ===");
send_byte(8'h48); // 'H'
send_byte(8'h65); // 'e'
send_byte(8'h6C); // 'l'
send_byte(8'h6C); // 'l'
send_byte(8'h6F); // 'o'
send_byte(8'h20); // ' '
send_byte(8'h55); // 'U'
send_byte(8'h41); // 'A'
send_byte(8'h52); // 'R'
send_byte(8'h54); // 'T'
// Wait for data to be received
#(BIT_PERIOD * 30);
verify_received_data();
// Test 3: Back-to-back transmission
$display("\n=== Test 3: Back-to-Back Transmission ===");
for (int i = 0; i < 16; i++) begin
send_byte(i);
end
// Wait for all data to be received
#(BIT_PERIOD * 50);
verify_received_data();
// Test 4: RX-only test (disconnect loopback)
$display("\n=== Test 4: RX-Only Test ===");
// Disconnect loopback for manual RX testing
force uart_rx = 1'b1;
#(CLK_PERIOD * 5);
// Manually send some bytes to RX
release uart_rx;
receive_byte(8'hA5);
receive_byte(8'h5A);
receive_byte(8'h96);
// Wait and check
#(BIT_PERIOD * 10);
// Final results
#(BIT_PERIOD * 20);
$display("\n=== Test Results ===");
$display("Total tests: %0d", test_count + 3); // +3 for RX-only tests
$display("Errors: %0d", error_count);
if (error_count == 0) begin
$display("✓ ALL TESTS PASSED!");
end else begin
$display("✗ %0d TESTS FAILED!", error_count);
end
$display("\n=== UART Testbench Complete ===");
$finish;
end
// Timeout watchdog
initial begin
#(BIT_PERIOD * 1000); // Long timeout
$display("ERROR: Testbench timeout!");
$finish;
end
// Optional: Dump waveforms
initial begin
$dumpfile("uart_tb.vcd");
$dumpvars(0, tb_uart_top);
end
endmodule
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Do you have any further questions? If not, we’ll go ahead and close this thread.
By the way, please note that we do not provide support for customized code. The code shared above is based on open-source examples and is provided as-is.
Thanks
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
As we do not receive any response from you on the previous answer that we have provided. Please login to ‘https://supporttickets.intel.com/s/?language=en_US’, view details of the desire request, and post a feed/response within the next 15 days to allow me to continue to support you. After 15 days, this thread will be transitioned to community support. The community users will be able to help you on your follow-up questions.

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page