FPGA, SoC, And CPLD Boards And Kits
FPGA Evaluation and Development Kits
6458 Discussions

UART functionality in altera max II EMP240 CPLD

srimathi
Novice
941 Views

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.

Labels (1)
0 Kudos
7 Replies
KennyTan_Altera
Moderator
810 Views

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)

);

```





0 Kudos
FvM
Honored Contributor II
734 Views

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

0 Kudos
srimathi
Novice
729 Views

Hi,

Is that code is working for you, I barely see any waveform on the tx line.

0 Kudos
KennyTan_Altera
Moderator
630 Views

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.


0 Kudos
KennyTan_Altera
Moderator
624 Views

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


0 Kudos
KennyTan_Altera
Moderator
491 Views

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


0 Kudos
KennyTan_Altera
Moderator
374 Views

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.



0 Kudos
Reply