diff --git a/axi_tb_top.sv b/axi_tb_top.sv new file mode 100644 index 0000000..433e3ad --- /dev/null +++ b/axi_tb_top.sv @@ -0,0 +1,42 @@ +// `include "uvm_pkg.sv" +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "axi_config_objs.svh" +`include "axi_interface.sv" +`include "axi_transaction.sv" +`include "axi_write_seq.sv" +`include "axi_read_seq.sv" +`include "axi_m_driver.sv" +`include "axi_m_monitor.sv" +`include "axi_master_agent.sv" +`include "axi_s_driver.sv" +`include "axi_s_monitor.sv" +`include "axi_slave_agent.sv" +`include "axi_scoreboard.sv" +`include "axi_env.sv" +`include "axi_test.sv" +`include "uvm_pkg.sv" + +// parameter A_WIDTH = 8; // Address bus width +// parameter D_WIDTH = 128; // Data bus width + +module top; + bit clk, rstn; + + always #5 clk = ~clk; + + initial rstn = 1; + + axi_intf#(.A_WIDTH(A_WIDTH), .D_WIDTH(D_WIDTH)) intf(clk, rstn); + env_config env_cfg; + + initial begin + env_cfg = new(); + env_cfg.intf = intf; + uvm_config_db#(env_config)::set(null, "uvm_test_top", "config", env_cfg); + uvm_config_db#(env_config)::set(null, "uvm_test_top.env.master", "config", env_cfg); + uvm_config_db#(env_config)::set(null, "uvm_test_top.env.slave", "config", env_cfg); + run_test("axi_base_test"); + end + +endmodule diff --git a/axi_test.sv b/axi_test.sv new file mode 100644 index 0000000..68d5687 --- /dev/null +++ b/axi_test.sv @@ -0,0 +1,166 @@ +class axi_base_test extends uvm_test; + `uvm_component_utils(axi_base_test) + + // Components + axi_env env; + axi_write_seq#(.D_WIDTH(D_WIDTH), .A_WIDTH(A_WIDTH)) wr_seq; + axi_read_seq#(.D_WIDTH(D_WIDTH), .A_WIDTH(A_WIDTH)) rd_seq; + + // variables + env_config env_cfg; + test_config test_cfg; + + function new(string name, uvm_component parent); + super.new(name, parent); + test_cfg = new("test_cfg"); + test_cfg.no_write_cases = 1; + test_cfg.no_read_cases = 1; + endfunction //new() + + // Function: build_phase + extern function void build_phase(uvm_phase phase); + + // Function: end_of_elaboration_phase + extern function void end_of_elaboration_phase(uvm_phase phase); + + // Function: run_phase + extern task run_phase(uvm_phase phase); + +endclass //axi_base_test extends uvm_test + +function void axi_base_test::build_phase(uvm_phase phase); + test_cfg.burst_type = 2; + uvm_config_db#(test_config)::set(null, "uvm_test_top.seq", "config", test_cfg); + + wr_seq = new("wr_seq"); + rd_seq = new("rd_seq"); + env = axi_env::type_id::create("env", this); +endfunction: build_phase + +function void axi_base_test::end_of_elaboration_phase(uvm_phase phase); + super.end_of_elaboration_phase(phase); + uvm_top.print_topology(); +endfunction: end_of_elaboration_phase + +task axi_base_test::run_phase(uvm_phase phase); + phase.raise_objection(this); + fork + wr_seq.start(env.master.w_seqr); + #200 + rd_seq.start(env.master.r_seqr); + // end + join + phase.drop_objection(this); +endtask: run_phase + + +// **************************************************************************************** +// Directed Test Cases +// **************************************************************************************** +class axi_write_test extends axi_base_test; + `uvm_component_utils(axi_write_test) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction //new() + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + endfunction: build_phase + + function void end_of_elaboration_phase(uvm_phase phase); + super.end_of_elaboration_phase(phase); + endfunction: end_of_elaboration_phase + + task run_phase(uvm_phase phase); + phase.raise_objection(this); + wr_seq.start(env.master.w_seqr); + phase.drop_objection(this); + endtask: run_phase +endclass //write_test extends axi_base_test + +class axi_read_test extends axi_base_test; + `uvm_component_utils(axi_read_test) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction //new() + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + endfunction: build_phase + + function void end_of_elaboration_phase(uvm_phase phase); + super.end_of_elaboration_phase(phase); + endfunction: end_of_elaboration_phase + + task run_phase(uvm_phase phase); + phase.raise_objection(this); + wr_seq.start(env.master.w_seqr); + // rd_seq.start(env.master.r_seqr); + phase.drop_objection(this); + endtask: run_phase +endclass //write_test extends axi_base_test + +class axi_fixed_test extends axi_base_test; + `uvm_component_utils(axi_fixed_test) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction //new() + + function void build_phase(uvm_phase phase); + test_cfg.burst_type = 0; + uvm_config_db#(test_config)::set(null, "uvm_test_top.seq", "config", test_cfg); + + wr_seq = new("wr_seq"); + rd_seq = new("rd_seq"); + env = axi_env::type_id::create("env", this); + endfunction: build_phase + + task run_phase(uvm_phase phase); + super.run_phase(phase); + endtask: run_phase +endclass //axi_fixed_test extends axi_base_test + +class axi_incr_test extends axi_base_test; + `uvm_component_utils(axi_incr_test) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction //new() + + function void build_phase(uvm_phase phase); + test_cfg.burst_type = 1; + uvm_config_db#(test_config)::set(null, "uvm_test_top.seq", "config", test_cfg); + + wr_seq = new("wr_seq"); + rd_seq = new("rd_seq"); + env = axi_env::type_id::create("env", this); + endfunction: build_phase + + task run_phase(uvm_phase phase); + super.run_phase(phase); + endtask: run_phase +endclass //axi_fixed_test extends axi_base_test + +class axi_wrap_test extends axi_base_test; + `uvm_component_utils(axi_wrap_test) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction //new() + + function void build_phase(uvm_phase phase); + test_cfg.burst_type = 2; + uvm_config_db#(test_config)::set(null, "uvm_test_top.seq", "config", test_cfg); + + wr_seq = new("wr_seq"); + rd_seq = new("rd_seq"); + env = axi_env::type_id::create("env", this); + endfunction: build_phase + + task run_phase(uvm_phase phase); + super.run_phase(phase); + endtask: run_phase +endclass //axi_fixed_test extends axi_base_test diff --git a/axi_transaction.sv b/axi_transaction.sv index e3166ad..045a081 100644 --- a/axi_transaction.sv +++ b/axi_transaction.sv @@ -1,125 +1,185 @@ -`include "uvm_macros.svh" -import uvm_pkg::*; - -class axi_seq_item extends uvm_seq_item; -//'uvm_object_utils(axi_seq_item); - -function new(string name="axi_seq_item") -super.new(name); -endfunction -//write adress channel signals - -rand bit wr_rd; - -rand bit [3:0] awid; -rand logic[31:0] awaddr; -rand logic[2:0] awsize; -rand logic[3:0] awlen; -rand logic[1:0] awburst; -rand logic[1:0] awlock; - -logic awvalid; -logic awready; - - - //write data channel - -rand bit [3:0] wid; -rand logic[31:0] wdata[][]; -logic[3:0] wstrb; -logic wlast; -logic wvalid; -logic wready; - -// write response channel -rand bit []3:0]bid; -logic[1:0] bresp; - -logic bvalid; -logic bready; - -// read adress channel - -rand bit [3:0] arid; - -rand logic[31:0] araddr; -rand logic[2:0] arsize; -rand logic[3:0] arlen; -rand logic[1:0] arburst; -rand logic[1:0] arlock; -logic arvalid; -logic arready; - -// read data channel - - -rand bit [3:0] rid; - logic[31:0] rdata; - logic[3:0]rresp; -logic rlast; -logic rvalid; -logic rready; - - -`uvm_object_utils_begin(axi_seq_item) - -`uvm_field_int(awid,UVM_ALL_ON) -`uvm_field_int(awaddr,UVM_ALL_ON) -`uvm_field_int(awsize,UVM_ALL_ON) -`uvm_field_int(awlen,UVM_ALL_ON) -`uvm_field_int(awburst,UVM_ALL_ON) -`uvm_field_int(awlock,UVM_ALL_ON) -`uvm_field_int(awvalid,UVM_ALL_ON) -`uvm_field_int(awready,UVM_ALL_ON) - -`uvm_field_int(wid,UVM_ALL_ON) -`uvm_field_int(wdata,UVM_ALL_ON) -`uvm_field_int(wlast,UVM_ALL_ON) -`uvm_field_int(wstrb,UVM_ALL_ON) -`uvm_field_int(wvalid,UVM_ALL_ON) -`uvm_field_int(wready,UVM_ALL_ON) - -`uvm_field_int(bid,UVM_ALL_ON) -`uvm_field_int(bresp,UVM_ALL_ON) -`uvm_field_int(bvalid,UVM_ALL_ON) -`uvm_field_int(bready,UVM_ALL_ON) - -`uvm_field_int(arid,UVM_ALL_ON) -`uvm_field_int(araddr,UVM_ALL_ON) -`uvm_field_int(arsize,UVM_ALL_ON) -`uvm_field_int(arlen,UVM_ALL_ON) -`uvm_field_int(arsize,UVM_ALL_ON) -`uvm_field_int(arburst,UVM_ALL_ON) -`uvm_field_int(arlock,UVM_ALL_ON) -`uvm_field_int(arvalid,UVM_ALL_ON) -`uvm_field_int(arready,UVM_ALL_ON) - -`uvm_field_int(rid,UVM_ALL_ON) -`uvm_field_int(rdata,UVM_ALL_ON) -`uvm_field_int(rlast,UVM_ALL_ON) -`uvm_field_int(rresp,UVM_ALL_ON) -`uvm_field_int(rvalid,UVM_ALL_ON) -`uvm_field_int(rready,UVM_ALL_ON) - -`uvm_object_utils_end - -//constraints for axi - -constraint aligned_addr{awadd%2**awsize==0;} - -constraint wrap_addr{solve awburst before awlen - if(awburst==2'b 10) - awlen inside{1,3,7,15};} - -constraint wid_sig{awid==wid;} - -constraint rid_sig{arid==rid;} - -constraint axi_4kb{awaddr%4096+(2**awsize*(awlen+1))<=4096;} - - - -//:constrain -endclass:axi_seq_item - - +import uvm_pkg::*; +typedef enum bit[1:0] { FIXED, INCR, WRAP } B_TYPE; +// Class: axi_transaction +// +class axi_transaction#(d_width = 16, a_width = 16) extends uvm_sequence_item; + typedef axi_transaction#(d_width, a_width) this_type_t; + `uvm_object_param_utils(axi_transaction#(d_width, a_width)); + + + // Group: Variables + bit [8:0] id; + rand bit [a_width-1:0] addr; + rand bit write; + rand bit [7:0] data [][]; + rand bit [2:0] b_size; + rand bit [3:0] b_len; + rand B_TYPE b_type; + bit b_last; + bit [1:0] b_resp; + bit [1:0] r_resp []; + + + + // Group: Constraints + constraint b_size_val { 8*(2**b_size) <= d_width; } + + constraint data_size { + /* solve order constraints */ + solve b_len before data; + solve b_size before data; + + /* rand variable constraints */ + data.size() == b_len+1; + foreach (data[i] ) + data[i].size() == 2**b_size; + } + + + constraint b_len_val { + /* solve order constraints */ + solve b_type before b_len; + + /* rand variable constraints */ + if(b_type == FIXED) + b_len inside { 0, 1 }; + else if(b_type == WRAP) + b_len inside { 1, 3, 7, 15 }; + } + + constraint addr_val { + /* solve order constraints */ + solve b_type before addr; + solve b_size before addr; + + /* rand variable constraints */ + if(b_type == WRAP) + addr == int'(addr/2**b_size) * 2**b_size; + } + + constraint addr_val_align { + /* solve order constraints */ + solve b_size before addr; + + /* rand variable constraints */ + addr == int'(addr/2**b_size) * 2**b_size; + } + + constraint addr_val_unalign { + /* solve order constraints */ + solve b_size before addr; + + /* rand variable constraints */ + addr != int'(addr/2**b_size) * 2**b_size; + } + + // Constructor: new + function new(string name = "axi_transaction"); + super.new(name); + endfunction: new + + // Function: do_copy + extern function void do_copy(uvm_object rhs); + // Function: do_compare + extern function bit do_compare(uvm_object rhs, uvm_comparer comparer); + // Function: convert2string + extern function string convert2string(); + // Function: do_print + extern function void do_print(uvm_printer printer); + // Function: do_record + // extern function void do_record(uvm_recorder recorder); + // Function: do_pack + // extern function void do_pack(); + // Function: do_unpack + // extern function void do_unpack(); + +endclass: axi_transaction + +/*----------------------------------------------------------------------------*/ +/* Functions */ +/*----------------------------------------------------------------------------*/ +function void axi_transaction::do_print(uvm_printer printer); + /* chain the print with parent classes */ + super.do_print(printer); + + /* list of local properties to be printed: */ + printer.print_field("ID", id, $bits(id), UVM_UNSIGNED); + printer.print_field("Addr", addr, $bits(addr), UVM_HEX); + printer.print_generic("Data", "dynamic array", 8*2**b_size*(b_len+1), $sformatf("%u", data)); + printer.print_field("Burst Size", b_size, $bits(b_size), UVM_UNSIGNED); + printer.print_field("Burst Length", b_len+1, $bits(b_len), UVM_UNSIGNED); + printer.print_generic("Burst Type", "B_TYPE", $bits(b_len), b_type.name()); +endfunction: do_print + +function string axi_transaction::convert2string(); + string s; + + /* chain the convert2string with parent classes */ + s = super.convert2string(); + + /* list of local properties to be printed: */ + // guide 0---4---8--12--16--20--24--28--32--36--40--44--48-- + s = {s, $sformatf("ID : %0d\n", id)}; + s = {s, $sformatf("Addr : 0x%0h\n", addr)}; + s = {s, $sformatf("Data : 0x%0u\n", data)}; + s = {s, $sformatf("Busrt Type : %s\n", b_type.name())}; + s = {s, $sformatf("Burst Size : %0d\n", b_size)}; + s = {s, $sformatf("Busrt Length : %0d\n", b_len+1)}; + s = {s, $sformatf("Busrt resp : 0x%0h\n", b_resp)}; + s = {s, $sformatf("Read resp : %0u\n", r_resp)}; + return s; +endfunction: convert2string + +function void axi_transaction::do_copy(uvm_object rhs); + this_type_t rhs_; + + if (!$cast(rhs_, rhs)) begin + `uvm_error({this.get_name(), ".do_copy()"}, "Cast failed!"); + return; + end + // `uvm_info({this.get_name(), ".do_copy()"}, "Cast succeded.", UVM_HIGH); + + /* chain the copy with parent classes */ + super.do_copy(rhs); + + /* list of local properties to be copied */ + this.id = rhs_.id; + this.addr = rhs_.addr; + this.data = rhs_.data; + this.b_type = rhs_.b_type; + this.b_size = rhs_.b_size; + this.b_len = rhs_.b_len; + this.b_resp = rhs_.b_resp; + this.r_resp = rhs_.r_resp; +endfunction: do_copy + +function bit axi_transaction::do_compare(uvm_object rhs, uvm_comparer comparer); + this_type_t rhs_; + + if (!$cast(rhs_, rhs)) begin + `uvm_error({this.get_name(), ".do_compare()"}, "Cast failed!"); + return 0; + end + // `uvm_info({this.get_name(), ".do_compare()"}, "Cast succeded.", UVM_HIGH); + + /* chain the compare with parent classes */ + do_compare = super.do_compare(rhs, comparer); + + /* list of local properties to be compared: */ + do_compare &= ( + this.id == rhs_.id && + this.addr == rhs_.addr && + this.b_type == rhs_.b_type && + this.b_size == rhs_.b_size && + this.b_len == rhs_.b_len && + this.b_resp == rhs_.b_resp + ); + + foreach(data[i,j]) begin + do_compare &= this.data[i][j] == rhs_.data[i][j]; + end + + foreach ( r_resp[i] ) begin + do_compare &= this.r_resp[i] == rhs_.r_resp[i]; + end +endfunction: do_compare \ No newline at end of file diff --git a/axi_write_seq.sv b/axi_write_seq.sv new file mode 100644 index 0000000..2f407d3 --- /dev/null +++ b/axi_write_seq.sv @@ -0,0 +1,61 @@ +// Class: axi_write_seq +// +class axi_write_seq#(D_WIDTH = 16, int A_WIDTH = 16) extends uvm_sequence; + `uvm_object_param_utils(axi_write_seq#(D_WIDTH, A_WIDTH)); + + // Group: Variables + const int no_of_trans; + bit[7:0] id; + axi_transaction#(D_WIDTH, A_WIDTH) trans; + test_config test_cfg; + + // Constructor: new + function new(string name = "axi_write_seq"); + super.new(name); + test_cfg = new("test_cfg"); + if(!uvm_config_db#(test_config)::get(null, "uvm_test_top.seq", "config", test_cfg)) + `uvm_fatal(get_name(), "config cannot be found in ConfigDB!") + + no_of_trans = test_cfg.no_write_cases; + endfunction: new + + // Task: body + // This is the user-defined task where the main sequence code resides. + extern virtual task body(); + +endclass: axi_write_seq + +task axi_write_seq::body(); + repeat(no_of_trans) begin + id++; + trans = axi_transaction#(D_WIDTH, A_WIDTH)::type_id::create("trans"); + if(test_cfg.isAligned) begin + trans.addr_val_align.constraint_mode(1); + trans.addr_val_unalign.constraint_mode(0); + trans.addr_val.constraint_mode(0); + end + else if (!test_cfg.isAligned) begin + trans.addr_val_align.constraint_mode(0); + trans.addr_val_unalign.constraint_mode(1); + trans.addr_val.constraint_mode(0); + end + else begin + trans.addr_val_align.constraint_mode(0); + trans.addr_val_unalign.constraint_mode(0); + trans.addr_val.constraint_mode(1); + end + start_item(trans); + if(test_cfg.burst_type == 0) + assert(trans.randomize() with { b_type == FIXED; }); + else if(test_cfg.burst_type == 1) + assert(trans.randomize() with { b_type == INCR; }); + else if(test_cfg.burst_type == 2) + assert(trans.randomize() with { b_type == WRAP; addr==16'h80c6;write==1;}); + else + assert(trans.randomize()); + trans.id = {1'b0, id}; + finish_item(trans); + trans.print(); + #10; + end +endtask: body