OpenCores
URL https://opencores.org/ocsvn/uart_fifo_cpu_if_sv_testbench/uart_fifo_cpu_if_sv_testbench/trunk

Subversion Repositories uart_fifo_cpu_if_sv_testbench

[/] [uart_fifo_cpu_if_sv_testbench/] [trunk/] [bench/] [uart_tb.sv] - Rev 4

Compare with Previous | Blame | View Log

//////////////////////////////////////////////////////////////////////
////                                                              ////
////  Testbench for uart.vhd                                      ////
////                                                              ////
////  This file is part of the XXX project                        ////
////  http://www.opencores.org/cores/xxx/                         ////
////                                                              ////
////  Description                                                 ////
////  Self checking testbench for uart.vhd. SV class implements a serial UART driver and monitor.
////  The driver accepts byte transactions and converts the byte to a serial stream.
////  The monitor converts serial UART bitstream to byte transactions.  
////                                                              ////
////  To Do:                                                      ////
////   -                                                          ////
////                                                              ////
////  Author(s):                                                  ////
////      - Andrew Bridger, andrew.bridger@gmail.com              ////
////                                                              ////
//////////////////////////////////////////////////////////////////////
////                                                              ////
//// Copyright (C) 2001 Authors and OPENCORES.ORG                 ////
////                                                              ////
//// This source file may be used and distributed without         ////
//// restriction provided that this copyright statement is not    ////
//// removed from the file and that any derivative work contains  ////
//// the original copyright notice and the associated disclaimer. ////
////                                                              ////
//// This source file is free software; you can redistribute it   ////
//// and/or modify it under the terms of the GNU Lesser General   ////
//// Public License as published by the Free Software Foundation; ////
//// either version 2.1 of the License, or (at your option) any   ////
//// later version.                                               ////
////                                                              ////
//// This source is distributed in the hope that it will be       ////
//// useful, but WITHOUT ANY WARRANTY; without even the implied   ////
//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      ////
//// PURPOSE.  See the GNU Lesser General Public License for more ////
//// details.                                                     ////
////                                                              ////
//// You should have received a copy of the GNU Lesser General    ////
//// Public License along with this source; if not, download it   ////
//// from http://www.opencores.org/lgpl.shtml                     ////
////                                                              ////
//////////////////////////////////////////////////////////////////////
//
// CVS Revision History
//
// $Log$
//

timeunit 1ns;
timeprecision 1ps;

import std_defs::*;

interface uart_if ();
  logic txd = 0; //from testbench driver
  logic rxd;     //to testbench monitor
endinterface : uart_if

typedef logic[11:0] cpu_addr_t;
typedef logic[7:0]  cpu_data_t;



module uart_tb();

`define BAUD_RATE      115200  //baud rate to test.
`define UART_FIFO_SIZE 32      //DUT FIFO size.
  
  //Instantiate the DUT interfaces;
  uart_if  uart_();
  //Keep Testbench simple for the CPU register interface - use tasks for bus functional model, so don't require SV i/f. 
  cpu_addr_t cpu_addr;
  cpu_data_t cpu_data_in;
  cpu_data_t cpu_data_out;
  logic cpu_we;
  logic cpu_rd;
  logic clk, reset;     
  
  //DUT
  \work.uart(rtl) vhdl_module (
                               .clk           (clk),
                               .reset         (reset),
                               //Serial UART
                               .i_rxd         (uart_.txd),
                               .o_txd         (uart_.rxd),
                               //Cpu register interface
                               .i_addr         (cpu_addr),
                               .i_write_enable (cpu_we),
                               .i_read_enable  (cpu_rd),
                               .i_data         (cpu_data_in),
                               .o_data         (cpu_data_out) );
  
  //Takes byte transactions and generates serial data on TXD. No parity, 8 data bits, 1 stop bit.
  class uart_driver;
    virtual uart_if  uart_;
    mailbox #(uint8) mbx;
    bit     verbose = false;
    uint16           baud_rate;  

    function new( virtual uart_if uart_, uint16 baud_rate, mailbox #(uint8) mbx );
      uart_     = uart_;
      baud_rate = baud_rate;
      mbx       = mbx;
    endfunction

    //Main driver thread. Empty the mailbox by looking for transactions, and driving them out onto TXD.
    task automatic run();
      //spawn processes required for this driver.
      fork begin
        uint8      data;
        logic[7:0] data_bits;

        forever begin
          mbx.get( data ); //block if no transactions waiting.
          data_bits = data; 
          //start bit
          uart_.txd = 0;
          #(1s/baud_rate);
          //8 bits of data
          for(uint8 i=0; i<8; i++) begin
            uart_.txd = data_bits[i]; //least significant bit first.
            #(1s/baud_rate);
          end
          //1 stop bit
          uart_.txd = 1;
          #(1s/baud_rate);
        end
      end
      join_none
    endtask 
  endclass
    
  //Looks for serial bytes on RXD, and converts them into byte transactions into a mailbox.
  class uart_monitor;
    virtual uart_if  uart_;
    mailbox #(uint8) mbx;
    bit     verbose = false;
    uint16           baud_rate;  

    function new( virtual uart_if uart_, uint16 baud_rate, mailbox #(uint8) mbx );
      uart_     = uart_;
      baud_rate = baud_rate;
      mbx       = mbx;
    endfunction

    //Main monitor thread. 
    task automatic run();
      fork begin
        forever begin
          //Look for a valid start bit. Must be at least 1/2 bit period duration.
          @(negedge uart_.rxd);
          #(0.5 * 1s/baud_rate);
          if ( uart_.rxd == 0 ) begin
            logic[7:0] data_bits;
            //read in 8 data bits, LSBit first, sampling in the center of the bit period.
            for(uint8 i=0; i<8; i++) begin
              #(1s/baud_rate);
              data_bits[i] = uart_.rxd;
            end
            //check stop bit.
            #(1s/baud_rate);
            if ( uart_.rxd != 1 ) begin
              $display("Monitor: Invalid stop bit.");
            end
            else begin
              //valid stop bit so generate transaction.
              uint8 data;
              data = data_bits;
              mbx.put( data );
            end
          end
        end
      end
      join_none
    endtask
  endclass

  task automatic cpu_init();
    cpu_addr    = 0;
    cpu_data_in = 0;
    cpu_we      = 0;
    cpu_rd      = 0;
  endtask
  
  //Read a register.
  task automatic cpu_read( cpu_addr_t addr, cpu_data_t data );
    @(negedge clk); //setup on falling edge. DUT reads on rising edge.
    cpu_addr = addr;
    cpu_rd   = 1;
    cpu_we   = 0;
    @(negedge clk);
    cpu_rd   = 0;
    @(negedge clk); //uart returns data maximum of 2 clocks after read enable.
    data = cpu_data_out;
  endtask

  //Write a register.
  task automatic cpu_write( cpu_addr_t addr, cpu_data_t data );
    @(negedge clk); //setup on falling edge. DUT reads on rising edge.
    cpu_addr    = addr;
    cpu_rd      = 0;
    cpu_we      = 1;
    cpu_data_in = data;
    @(negedge clk);
  endtask

  //System clock
  initial
    begin
      clk = 0;
      forever #20ns clk <= ~clk;
    end

  //Main sim process.
  initial
    begin
      $display("%t << Starting the simulation >>", $time);
      cpu_init(); 
        
      //ok, now run some tests.
      test_cpu_interface(); 
      test_serial_facing_loopback(); //This one is also a good test for the testbench UART monitor/driver classes. 
      test_cpu_to_txd();
      //test_rxd_to_cpu();
      
      $display("%m: %t << Simulation ran to completion >>", $time);
      //     $stop(0); //stop the simulation
    end
  
  task automatic dut_reset();
    reset <= 1;
    repeat(16) @(negedge clk);
    reset <= 0;
  endtask
  
  uart_driver  Uart_driver;
  uart_monitor Uart_monitor;
  
  task automatic build_test_harness( mailbox #(uint8) uart_driver_mbx, mailbox #(uint8) uart_monitor_mbx);
    //transaction mailboxes for comms between driver/monitor and main testbench thread.
    uart_driver_mbx  = new(0); //unbounded
    uart_monitor_mbx = new(0); //unbounded
    
    //construct UART monitor and driver.
    Uart_driver  = new( uart_, `BAUD_RATE, uart_driver_mbx );
    Uart_monitor = new( uart_, `BAUD_RATE, uart_monitor_mbx );

    //start them up.
    Uart_driver.run();
    Uart_monitor.run();
  endtask
  
  task automatic test_cpu_interface();
    cpu_data_t readval;
    $display("Testing cpu register interface...");

    dut_reset();
    //test read/write baud rate register
    cpu_write( 'h002, 'h55 );
    cpu_write( 'h003, 'hAA );
    cpu_read(  'h002, readval );
    assert (readval == 'h55) else $display("Failed to readback register 2 correctly.");
    cpu_read(  'h003, readval );
    assert (readval == 'hAA) else $display("Failed to readback register 2 correctly.");
  endtask

  task automatic test_serial_facing_loopback();
    //This loopback is right at the UART txd/rxd pins.
    mailbox #(uint8) sent_data_mbx;
    mailbox #(uint8) uart_driver_mbx;
    mailbox #(uint8) uart_monitor_mbx;
    build_test_harness( uart_driver_mbx, uart_monitor_mbx);
    dut_reset();

    $display("Testing serial facing loopback...");
    cpu_write( 'h002, 'h80 ); //enable the loopback.

    //Send a bunch of serial UART bytes. 
    repeat(1000) begin
      uint8 data;
      data = $random();
      uart_driver_mbx.put( data );
      sent_data_mbx.put( data );
    end

    //Gives some time for any remaining transactions to propagate through the DUT.
    #( `UART_FIFO_SIZE * (12 * 1s/`BAUD_RATE)); //10 bits/ baud, but allow for 12.

    //Check the sent data comes back error free.
    compare_mailbox_data( sent_data_mbx, uart_monitor_mbx );
  endtask
               
  task automatic test_cpu_to_txd();
    mailbox #(uint8) sent_data_mbx;

    mailbox #(uint8) uart_driver_mbx;
    mailbox #(uint8) uart_monitor_mbx;
    build_test_harness( uart_driver_mbx, uart_monitor_mbx);
    dut_reset();
    
    sent_data_mbx = new(0);
    $display("Testing cpu to UART serial TXD interface...");
    
    //Using the UART's cpu interface, write a bunch of data in. Make sure we don't overflow the uart tx FIFO by
    //always reading the FIFO full flag prior to writing. Check all data is correctly received by the monitor.
    repeat(1000) begin
      uint8 readval;
      uint8 data;
      data = $random();
      //check uart tx fifo is not full
      cpu_read( 'h001, readval );
      if ((readval & 'h08) == 0) begin
        cpu_write( 'h000, data );
        sent_data_mbx.put(data);
      end
    end

    //Gives some time for any remaining transactions to propagate through the DUT.
    #( `UART_FIFO_SIZE * (12 * 1s/`BAUD_RATE)); //10 bits/ baud, but allow for 12.
    
    //Check the sent and received data is the same.
    compare_mailbox_data( sent_data_mbx, uart_monitor_mbx );
  endtask

  //Check all reference mailbox items against dut mailbox items. Expected to be identical, report differences.
  function automatic void compare_mailbox_data( mailbox #(uint8) ref_mbx, mailbox #(uint8) dut_mbx );
    uint32 error = 0;
    uint32 good = 0;
    uint32 ref_mbx_num;
    uint32 dut_mbx_num;

    ref_mbx_num = ref_mbx.num();
    dut_mbx_num = dut_mbx.num();
    
    repeat( ref_mbx_num ) begin
      uint8 dut_data;
      uint8 ref_data;
      uint32 tryget_result;
      
      ref_mbx.try_get( ref_data );
      //try to get dut data, may not be there if dut swallowed it.
      tryget_result = dut_mbx.try_get( dut_data );
      if (tryget_result) begin
        if (ref_data != dut_data) begin
          error++;
        end
        else begin
          good++;
        end
        break; //no more DUT data
      end
    end
    $display("Good: %2d, Errored: %2d, Excess reference %2d, Excess DUT %2d", 
             good, error, ref_mbx_num, dut_mbx_num);
  endfunction
  
endmodule 



   

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.