Forum Discussion
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