天天看點

uart接口用systemverilog實作

1.發送子產品

module uart_tx(clk,rst,start,tx_data_in,tx,tx_active,done_tx);

parameter clk_freq = 50000000; //MHz
parameter baud_rate = 19200; //bits per second
input clk,rst;
input start;
input [7:0] tx_data_in;
output tx;
output tx_active;
output logic done_tx;

localparam clock_divide = (clk_freq/baud_rate);//分頻

enum bit [2:0]{ tx_IDLE = 3'b000,
                tx_START = 3'b001,
		        tx_DATA = 3'b010,
	            tx_STOP = 3'b011,
		        tx_DONE = 3'b100 } tx_STATE, tx_NEXT;//狀态機的五個狀态
					 
logic [11:0] clk_div_reg,clk_div_next;//分頻計數
logic [7:0] tx_data_reg;//目前發送的資料
logic [7:0] tx_data_next;//下一個發送的資料
logic tx_out_reg;//目前發送的單bit資料
logic tx_out_next;//下一個發送的單bit資料
logic [2:0] index_bit_reg,index_bit_next;//發送bit的索引

assign tx_active = (tx_STATE == tx_DATA);//隻有為發送狀态時,tx_active==1
assign tx = tx_out_reg;

always_ff @(posedge clk) begin
if(rst) begin
tx_STATE <= tx_IDLE;
clk_div_reg <= 0;
tx_out_reg <= 0;
tx_data_reg <= 0;
index_bit_reg <= 0;
end
else begin
tx_STATE <= tx_NEXT;
clk_div_reg <= clk_div_next;
tx_out_reg <= tx_out_next;
tx_data_reg <= tx_data_next;
index_bit_reg <= index_bit_next;
end
end

always @(*) begin
tx_NEXT = tx_STATE;
clk_div_next = clk_div_reg;
tx_out_next = tx_out_reg;
tx_data_next = tx_data_reg;
index_bit_next = index_bit_reg;
done_tx = 0;

case(tx_STATE)

tx_IDLE: begin
tx_out_next = 1;
clk_div_next = 0;
index_bit_next = 0;
if(start == 1) begin//start=1,開始發送
tx_data_next = tx_data_in;
tx_NEXT = tx_START;
end
else begin
tx_NEXT = tx_IDLE;
end
end

tx_START: begin
tx_out_next = 0;//拉低一個電平,表示起始位
if(clk_div_reg < clock_divide-1) begin
clk_div_next = clk_div_reg + 1'b1;
tx_NEXT = tx_START;
end
else begin
clk_div_next = 0;
tx_NEXT = tx_DATA;
end
end

tx_DATA: begin
tx_out_next = tx_data_reg[index_bit_reg];//開始發送資料位
if(clk_div_reg < clock_divide-1) begin
clk_div_next = clk_div_reg + 1'b1;
tx_NEXT = tx_DATA;
end
else begin
clk_div_next = 0;
if(index_bit_reg < 7) begin
index_bit_next = index_bit_reg + 1'b1;
tx_NEXT = tx_DATA;
end
else begin
index_bit_next = 0;
tx_NEXT = tx_STOP; 
end
end
end

tx_STOP: begin
tx_out_next = 1;//停止位,拉高一個電平
if(clk_div_reg < clock_divide-1) begin
clk_div_next = clk_div_reg + 1'b1;
tx_NEXT = tx_STOP;
end
else begin
clk_div_next = 0;
tx_NEXT = tx_DONE;
end
end

tx_DONE: begin
done_tx = 1;
tx_NEXT = tx_IDLE;
end

default: tx_NEXT = tx_IDLE;
endcase
end

endmodule 
           

2.接收子產品

module uart_rx(clk,rst,rx,rx_data_out);

parameter clk_freq = 50000000; //MHz
parameter baud_rate = 19200; //bits per second
input clk;
input rst;
input rx;
output [7:0] rx_data_out;

localparam clock_divide = (clk_freq/baud_rate);

enum bit [2:0] { rx_IDLE = 3'b000,
                 rx_START = 3'b001,
		 rx_DATA = 3'b010,
		 rx_STOP = 3'b011,
		 rx_DONE = 3'b100 } rx_STATE, rx_NEXT;
					 
logic [11:0] clk_div_reg,clk_div_next;//分頻計數
logic [7:0] rx_data_reg,rx_data_next;
logic [2:0] index_bit_reg,index_bit_next;//bit索引


always_ff @(posedge clk) begin
if(rst) begin
rx_STATE <= rx_IDLE;
clk_div_reg <= 0;
rx_data_reg <= 0;
index_bit_reg <= 0;
end
else begin
rx_STATE <= rx_NEXT;
clk_div_reg <= clk_div_next;
rx_data_reg <= rx_data_next;
index_bit_reg <= index_bit_next;
end
end

always @(*) begin
rx_NEXT = rx_STATE;
clk_div_next = clk_div_reg;
rx_data_next = rx_data_reg;
index_bit_next = index_bit_reg;

case(rx_STATE)					 

rx_IDLE: begin
clk_div_next = 0;
index_bit_next = 0;
if(rx == 0) begin//接收到低電平,表示接收開始
rx_NEXT = rx_START;
end
else begin
rx_NEXT = rx_IDLE;
end
end

rx_START: begin
if(clk_div_reg == (clock_divide-1)/2) begin
if(rx == 0) begin
clk_div_next = 0;
rx_NEXT = rx_DATA;
end
else begin
rx_NEXT = rx_IDLE;
end
end
else begin
clk_div_next = clk_div_reg + 1'b1;
rx_NEXT = rx_START;
end
end

rx_DATA: begin
if(clk_div_reg < clock_divide-1) begin
clk_div_next = clk_div_reg + 1'b1;
rx_NEXT = rx_DATA;
end
else begin
clk_div_next = 0;
rx_data_next[index_bit_reg] = rx;//接收資料
if(index_bit_reg < 7) begin
index_bit_next = index_bit_reg + 1'b1;
rx_NEXT = rx_DATA;
end
else begin
index_bit_next = 0;
rx_NEXT = rx_STOP;
end
end
end

rx_STOP: begin
if(clk_div_reg < clock_divide - 1) begin
clk_div_next = clk_div_reg + 1'b1;
rx_NEXT = rx_STOP;
end
else begin
clk_div_next = 0;
rx_NEXT = rx_DONE;
end
end

rx_DONE: begin
rx_NEXT = rx_IDLE;
end

default: rx_NEXT = rx_IDLE;
endcase
end

assign rx_data_out = rx_data_reg;

endmodule
           

3.頂層子產品

module uart(clk,rst,rx,tx_data_in,start,rx_data_out,tx,tx_active,done_tx);

parameter clk_freq = 50000000; //MHz
parameter baud_rate = 19200; //bits per second
parameter clock_divide = (clk_freq/baud_rate);

  input clk,rst; 
  input rx;
  input [7:0] tx_data_in;
  input start;
  output tx; 
  output [7:0] rx_data_out;
  output tx_active;
  output done_tx;
	
	
uart_rx 
       #(.clk_freq(clk_freq),
	 .baud_rate(baud_rate)
	)
      receiver
             (
              .clk(clk),
	      .rst(rst),
	      .rx(rx),
	      .rx_data_out(rx_data_out)
             );


uart_tx 
       #(.clk_freq(clk_freq),
	 .baud_rate(baud_rate)
        )
      transmitter			 
               (               
                .clk(clk),
		.rst(rst),
		.start(start),
		.tx_data_in(tx_data_in),
		.tx(tx),
		.tx_active(tx_active),
		.done_tx(done_tx)
               );

endmodule
           

基于UVM的驗證

1.定義接口

interface uart_intf;
  
  logic clk,rst;
  //接收端口
  logic rx;
  logic [7:0] rx_data_out;
  //發送端口
  logic [7:0] tx_data_in;
  logic start;
  logic tx;
  logic tx_active;
  logic done_tx; 

endinterface
           

2.transaction

`include "uvm_macros.svh"
import uvm_pkg::*;

  class uart_trans extends uvm_sequence_item;
   
  
    `uvm_object_utils(uart_trans)
         
     bit rx;
	 bit [7:0] rx_data_out;
	 bit start;
	 bit tx;
	   
	 rand bit [7:0] tx_data_in;
	 bit tx_active;
	 bit done_tx;
  
   
    function new (string name = "uart_trans");
      super.new(name);
    endfunction
 endclass: uart_trans
           

3.覆寫率收集

class uart_cov extends uvm_subscriber #(uart_trans);//繼承uvm_subscriber,該類自帶了一個uvm_analysis_imp analysis_export端口
  
  `uvm_component_utils(uart_cov)
  uart_trans trans;
	

  covergroup cov_inst;//定義覆寫組
  RX:coverpoint trans.rx {option.auto_bin_max = 1;}//定義bin的數量
  TX_DIN:coverpoint trans.tx_data_in {option.auto_bin_max = 8;}
  START:coverpoint trans.start {option.auto_bin_max = 1;}
  TX:coverpoint trans.tx {option.auto_bin_max = 1;}
  RX_DOUT:coverpoint trans.rx_data_out {option.auto_bin_max = 8;}
  TX_ACT:coverpoint trans.tx_active {option.auto_bin_max = 1;}
  DONE:coverpoint trans.done_tx {option.auto_bin_max = 1;}
  
  RXxRX_DOUT: cross RX,RX_DOUT;//交叉覆寫
  TXxTX_DINxTX_ACTxDONE: cross TX,TX_DIN,TX_ACT,DONE;
  STARTxTX_DIN: cross START,TX_DIN;
  endgroup 
  
  
  function new(string name="", uvm_component parent);
		super.new(name, parent);
		cov_inst = new();//建立覆寫組對象
	endfunction

	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
	endfunction


  	virtual function void write(uart_trans t);//在分析imp端口定義write方法
  	$cast(trans, t);
	 cov_inst.sample();
	 endfunction

endclass
           

4.sequence

typedef uvm_sequencer #(uart_trans) uart_sequencer;//sequencer直接使用uvm_sequencer

class uart_sequence extends uvm_sequence #(uart_trans);
  
    `uvm_object_utils(uart_sequence)
    int count;
    
    function new (string name = ""); 
      super.new(name);
    endfunction

    task body;
      if (starting_phase != null)
        starting_phase.raise_objection(this);
        void'(uvm_config_db #(int)::get(null,"","no_of_transactions",count));
      repeat(count)
      begin
        req = uart_trans::type_id::create("req");//發送item
        start_item(req);
        if( !req.randomize() )
          `uvm_error("", "Randomize failed")
        finish_item(req);
      end
      
      if (starting_phase != null)
        starting_phase.drop_objection(this);
    endtask: body
   
  endclass: uart_sequence
           

5.driver

class uart_driver extends uvm_driver #(uart_trans);
  
    `uvm_component_utils(uart_driver)

    parameter clk_freq = 50000000; //MHz
    parameter baud_rate = 19200; //bits per second
    localparam clock_divide = (clk_freq/baud_rate);

    virtual uart_intf vif;
    reg [7:0] data;
    int no_transactions;
    
    
    function new(string name, uvm_component parent);
      super.new(name, parent);
    endfunction
    
    virtual function void build_phase(uvm_phase phase);
      // Get interface reference from config database
      if( !uvm_config_db #(virtual uart_intf)::get(this, "", "uart_intf", vif) )//擷取虛接口
        `uvm_error("", "uvm_config_db::get failed")
    endfunction 
   
    virtual task void run_phase(uvm_phase phase);
	  super.run_phase(phase);
      forever
      begin
        seq_item_port.get_next_item(req);

        
      `uvm_info("","---------------------------------------------",UVM_MEDIUM) 
      `uvm_info("", $sformatf("\t Transaction No. = %0d",no_transactions),UVM_MEDIUM) 
      //Test tx 驗證發送
      vif.start <= 1;
      vif.rx <= 1;
      @(posedge vif.clk);
      vif.tx_data_in <= req.tx_data_in;
      @(posedge vif.clk);
      wait(vif.done_tx == 1);
      vif.start <= 0;
      if(vif.done_tx == 1) begin
      `uvm_info("", $sformatf("\t start = %0b, \t tx_data_in = %0h,\t done_tx = %0b",vif.start,req.tx_data_in,vif.done_tx),UVM_MEDIUM)  
      `uvm_info("","[TRANSACTION]::TX PASS",UVM_MEDIUM)  
       end
      else begin
      `uvm_info("", $sformatf("\t start = %0b, \t tx_data_in = %0h,\t done_tx = %0b",vif.start,req.tx_data_in,vif.done_tx),UVM_MEDIUM)  
      `uvm_info("","[TRANSACTION]::TX PASS",UVM_MEDIUM)  
       end  
      repeat(100) @(posedge vif.clk);
      //Test rx 驗證接收
	    @(posedge vif.clk);
	    data = $random;
	    vif.rx <= 1'b0;
	    repeat(clock_divide) @(posedge vif.clk);
	    for(int i=0;i<8;i++) 
		begin
	    vif.rx <= data[i];
	    repeat(clock_divide) @(posedge vif.clk);
	    end
	    vif.rx <= 1'b1;
	    repeat(clock_divide) @(posedge vif.clk);
	    repeat(100) @(posedge vif.clk); 
	   `uvm_info("", $sformatf("\t Expected data = %0h, \t Obtained data = %0h", data,vif.rx_data_out),UVM_MEDIUM)  
      begin
	    if(vif.rx_data_out == data) begin
	    `uvm_info("","[TRANSACTION]::RX PASS",UVM_MEDIUM)  
      `uvm_info("","---------------------------------------------",UVM_MEDIUM)  
       end
	    else begin 
	   `uvm_info("","[TRANSACTION]::RX FAIL",UVM_MEDIUM)  
     `uvm_info("","---------------------------------------------",UVM_MEDIUM)  
      end
  end
                
        seq_item_port.item_done();
        no_transactions++;
      end
    endtask

  endclass: uart_driver
           

6.monitor

class uart_mon extends uvm_monitor;
	
	virtual uart_intf intf;
	uart_trans trans;
	uvm_analysis_port #(uart_trans) ap_port;//分析端口
	`uvm_component_utils(uart_mon)
	
	function new(string name="", uvm_component parent);
		super.new(name, parent);
	endfunction


	function void build_phase(uvm_phase phase);
	  super.build_phase(phase);
	  ap_port = new("ap_port",this);
	  //trans = uart_trans::type_id::create("trans");
		if(!uvm_config_db #(virtual uart_intf)::get(this, "", "uart_intf", intf)) 
		   begin
		    `uvm_error("ERROR::", "UVM_CONFIG_DB FAILED in uart_mon")
		    end
		//ap_port = new("ap_port", this);
	endfunction

  
  task run_phase(uvm_phase phase);
    super.run_phase(phase);
    while(1) begin
      @(posedge intf.clk);
      trans = uart_trans::type_id::create("trans");
      trans.start = intf.start;
      trans.tx_active = intf.tx_active;
      trans.done_tx = intf.done_tx;
      trans.tx_data_in = intf.tx_data_in;
      trans.rx = intf.rx;
      trans.rx_data_out = intf.rx_data_out;
      trans.tx = intf.tx;
      ap_port.write(trans);
    end
  endtask
  
  
endclass
           

7.agent

class uart_agent extends uvm_agent;
	
 `uvm_component_utils(uart_agent)
	  
	  uart_sequencer seqr;
    uart_driver    driv;
    uart_mon mon;
    uart_cov cov;
    
    function new(string name = "", uvm_component parent);
      super.new(name, parent);
    endfunction
 
    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      seqr = uart_sequencer::type_id::create("seqr", this);
      driv = uart_driver::type_id::create("driv", this);
      mon = uart_mon::type_id::create("mon", this);
      cov = uart_cov::type_id::create("cov", this);
    endfunction
    
    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      driv.seq_item_port.connect( seqr.seq_item_export);
      mon.ap_port.connect(cov.analysis_export);//連接配接monitor和subscriber
    endfunction
    

endclass
           

 8.environment

class uart_env extends uvm_env;

  `uvm_component_utils(uart_env)
    
   uart_agent agent;
    
    function new(string name = "", uvm_component parent);
      super.new(name, parent);
    endfunction
 
    function void build_phase(uvm_phase phase);
	super.build_phase(phase);
    agent = uart_agent::type_id::create("agent",this);  
    endfunction
    
    
  endclass: uart_env
           

9.test

class uart_test extends uvm_test;
  
    `uvm_component_utils(uart_test)
    
    uart_env env;
    
    function new(string name = "", uvm_component parent);
      super.new(name, parent);
    endfunction
    
    function void build_phase(uvm_phase phase);
	   super.build_phase(phase);
      env = uart_env::type_id::create("env", this);
    endfunction
    
    	function void end_of_elaboration_phase(uvm_phase phase);
			//`uvm_info(uvm_get_fullname(), this.sprint(), UVM_NONE)
			`uvm_info("", this.sprint(), UVM_NONE)
		endfunction
    
    task run_phase(uvm_phase phase);
      uart_sequence seqr;
      seqr = uart_sequence::type_id::create("seqr");//建立對象并啟動sequence
      //if( !seqr.randomize() ) 
        //`uvm_error("", "Randomize failed")
      seqr.starting_phase = phase;
      seqr.start( env.agent.seqr );
    endtask
     
  endclass: uart_test
           

10.頂層子產品

`include "uart_trans.sv"
`include "uart_sequence.sv"
`include "uart_intf.sv"
`include "uart_driver.sv"
`include "uart_mon.sv"
`include "uart_cov.sv"
`include "uart_agent.sv"
`include "uart_env.sv"
`include "uart_test.sv"

module tb_uart_top;
  
  
  bit clk;
  bit rst;
  
  uart_intf intf();
  
  uart    dut(
              .clk(intf.clk),
              .rst(intf.rst),
              .rx(intf.rx),
              .tx_data_in(intf.tx_data_in),
              .start(intf.start),
              .rx_data_out(intf.rx_data_out),
              .tx(intf.tx),
              .tx_active(intf.tx_active),
              .done_tx(intf.done_tx)
              );

  // Clock generator
  initial
  begin
    intf.clk = 0;
    forever #5 intf.clk = ~intf.clk;
  end
  
  initial
  begin
    intf.rst = 1;
    #1000;
    intf.rst = 0;
  end



  initial
  begin
    uvm_config_db #(virtual uart_intf)::set(null, "*", "uart_intf", intf);
    void'(uvm_config_db #(int)::set(null,"*","no_of_transactions",10));
    
    uvm_top.finish_on_completion = 1;
    
    run_test("uart_test");
  end

endmodule: tb_uart_top
           

繼續閱讀