├── README ├── dpi ├── Makefile ├── dpi_mem.c ├── mem.c └── mem.h ├── examples ├── virtual_master_to_dut_slave │ ├── demo_axi_master_seq_lib.sv │ ├── demo_axi_slave_seq_lib.sv │ ├── demo_conf.sv │ ├── demo_lib.sv │ ├── demo_pkg.sv │ ├── demo_scoreboard.sv │ ├── demo_tb.sv │ ├── demo_top.sv │ └── dut_dummy.v └── virtual_master_to_virtual_slave │ ├── demo_axi_master_seq_lib.sv │ ├── demo_axi_slave_seq_lib.sv │ ├── demo_conf.sv │ ├── demo_lib.sv │ ├── demo_pkg.sv │ ├── demo_scoreboard.sv │ ├── demo_tb.sv │ ├── demo_top.sv │ └── dut_dummy.v ├── pli ├── dram.c └── vpi_user.c ├── run.py ├── sv ├── axi_assertions.sv ├── axi_common.sv ├── axi_conf.sv ├── axi_coverage.sv ├── axi_env.sv ├── axi_master_agent.sv ├── axi_master_driver.sv ├── axi_master_monitor.sv ├── axi_master_recorder.sv ├── axi_master_sequencer.sv ├── axi_pat.sv ├── axi_pkg.sv ├── axi_slave_agent.sv ├── axi_slave_driver.sv ├── axi_slave_monitor.sv ├── axi_slave_recorder.sv ├── axi_slave_sequencer.sv ├── axi_transfer.sv ├── axi_type.sv ├── axi_vif.sv └── sequence_libs │ ├── axi_based_seq_lib.sv │ ├── axi_master_based_seq_lib.sv │ ├── axi_master_read_seq_lib.sv │ ├── axi_master_write_seq_lib.sv │ ├── axi_seq_lib_pkg.sv │ └── axi_slave_based_seq_lib.sv └── v └── axi_slave.v /README: -------------------------------------------------------------------------------- 1 | 2 | /*/////////////////////////////////////////////////////////////////// 3 | //// //// 4 | //// Author: Sean Chen //// 5 | //// funningboy@gmail.com //// 6 | //// //// 7 | //// //// 8 | ///////////////////////////////////////////////////////////////////// 9 | //// //// 10 | //// Copyright (C) 2013 //// 11 | //// //// 12 | //// This source file may be used and distributed without //// 13 | //// restriction provided that this copyright statement is not //// 14 | //// removed from the file and that any derivative work contains //// 15 | //// the original copyright notice and the associated disclaimer.//// 16 | //// //// 17 | //// This source file is free software; you can redistribute it //// 18 | //// and/or modify it under the terms of the GNU Lesser General //// 19 | //// Public License as published by the Free Software Foundation.//// 20 | //// //// 21 | //// This source is distributed in the hope that it will be //// 22 | //// useful, but WITHOUT ANY WARRANTY; without even the implied //// 23 | //// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// 24 | //// PURPOSE. See the GNU Lesser General Public License for more//// 25 | //// details. http://www.gnu.org/licenses/lgpl.html //// 26 | //// //// 27 | ///////////////////////////////////////////////////////////////////*/ 28 | 29 | AXI BFM standard protocol checker 30 | supported 31 | read/write phase check 32 | normal protocol check (valid/ready) 33 | id tag mapping check (addr/data/resp) has the same id 34 | transfer read/write check, (only for slave side) like bist check 35 | TLM analysis port, user can use it to hook their third part golden design. like SystemC TLM 2.0 port or DPI interface ..., 36 | unsupported 37 | Qos(priority) 38 | region 39 | user info 40 | cache 41 | protect ... 42 | atomic check 43 | 44 | folder 45 | ./dpi : SystemVerilog DPI interface 46 | ./examples : example 47 | ./pli : Verilig PLI interface 48 | ./v : Design under test (DUT) 49 | ./sv : SystemVerilog UVM class 50 | ./log : simulation results 51 | ./rpt : simulation rpt 52 | 53 | How to run the example (1 virtual master to 1 DUT slave) 54 | 55 | 1. set up your test env 56 | 1.1 prepare your DUT and link it to our test env, 57 | for example we use 1 Virtual Master to 1 DUT slave 58 | 59 | check ./v/axi_slave.v # dut slave 60 | check ./example/virtual_master_to_dut_slave/dut_dummy.v # link dut to test env 61 | check ./example/virtual_master_to_dut_slave/demo_top.sv # top module contains DUT and Tester 62 | 63 | 1.2 Memory map table, how many Masters/Slaves, Master/Slave connection .... 64 | 65 | check ./examples/virtual_master_to_dut_slave/demo_conf.sv 66 | 67 | 1.3 define your test suites 68 | in this example, we use 1 read after 1 write to check the memory can read/write supported 69 | 70 | check ./examples/virtual_master_to_dut_slave/demo_axi_master_seq_lib.sv 71 | check ./examples/virtual_master_to_dut_slave/demo_lib.sv 72 | check ./examples/virtual_master_to_dut_slave/demo_tb.sv 73 | 74 | 2. how to run it 75 | tools requirement 76 | irun > Version 10 77 | python > Version 2.7 78 | python ./run.py 79 | 80 | 81 | -------------------------------------------------------------------------------- /dpi/Makefile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/funningboy/uvm_axi/8b4dd7fd607a2a64de90ec8119c6eecd07903572/dpi/Makefile -------------------------------------------------------------------------------- /dpi/dpi_mem.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/funningboy/uvm_axi/8b4dd7fd607a2a64de90ec8119c6eecd07903572/dpi/dpi_mem.c -------------------------------------------------------------------------------- /dpi/mem.c: -------------------------------------------------------------------------------- 1 | 2 | //#include 3 | //using namespace std; 4 | // 5 | ///*--------------------- 6 | // * hash table 7 | // ----------------------*/ 8 | template 9 | class MemTable { 10 | public : 11 | 12 | MemTable() {} 13 | ~MemTable() {} 14 | 15 | private: 16 | typedef typename std::map TMap; 17 | TMap m_map; 18 | 19 | } 20 | 21 | int main(int argc, char *argv[]) 22 | { 23 | // MemTable m; 24 | return 0; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /dpi/mem.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/funningboy/uvm_axi/8b4dd7fd607a2a64de90ec8119c6eecd07903572/dpi/mem.h -------------------------------------------------------------------------------- /examples/virtual_master_to_dut_slave/demo_axi_master_seq_lib.sv: -------------------------------------------------------------------------------- 1 | // testsuite sets.... 2 | 3 | `ifndef DEMO_AXI_MASTER_SEQ_LIB_SV 4 | `define DEMO_AXI_MASTER_SEQ_LIB_SV 5 | 6 | 7 | //--------------------------------------------- 8 | // SEQUENCE: DEMO_AXI_master_write_data_after_write_addr 9 | //--------------------------------------------- 10 | class DEMO_AXI_master_write_data_after_write_addr extends AXI_master_base_seq; 11 | 12 | AXI_master_write_seq m_wr_seq; 13 | AXI_master_read_seq m_rd_seq; 14 | 15 | int unsigned m_inc_addr = 0; 16 | int unsigned m_inc_data = 0; 17 | int unsigned m_inc_id = 0; 18 | 19 | `uvm_object_utils(DEMO_AXI_master_write_data_after_write_addr) 20 | 21 | function new(string name="axi_master_write_data_after_write_addr"); 22 | super.new(name); 23 | endfunction 24 | 25 | 26 | virtual task sent_write_trx(); 27 | 28 | m_inc_addr = 0; 29 | m_inc_data = 0; 30 | m_inc_id = 0; 31 | 32 | repeat(5) begin 33 | 34 | m_wr_seq = new ( 35 | .name ("axi_master_write_data_after_write_addr"), 36 | .addr (32'h0000_1000 + m_inc_addr), 37 | .ibyte (BYTE_4), 38 | .len (LEN_4), 39 | .burst (INCR), 40 | .id (1 + m_inc_id), 41 | .data ({ 32'h0000_0001 + m_inc_data, 42 | 32'h0000_0002 + m_inc_data, 43 | 32'h0000_0003 + m_inc_data, 44 | 32'h0000_0004 + m_inc_data} ), 45 | .strb ({4'b1111, 46 | 4'b1111, 47 | 4'b1111, 48 | 4'b1111 } ), 49 | .addr_delay (0), 50 | .data_delay (5), // write data after write addr 51 | .resp_delay (0) 52 | ); 53 | 54 | start_item(m_wr_seq); 55 | finish_item(m_wr_seq); 56 | 57 | m_inc_addr += 32'h0001_0000; 58 | m_inc_data += 32'h0001_0000; 59 | m_inc_id += 1; 60 | end 61 | 62 | endtask : sent_write_trx 63 | 64 | 65 | virtual task sent_read_trx(); 66 | 67 | m_inc_addr = 0; 68 | m_inc_data = 0; 69 | m_inc_id = 0; 70 | 71 | repeat(5) begin 72 | 73 | m_rd_seq = new( 74 | .name ("axi_master_write_data_after_write_addr"), 75 | .addr (32'h0000_1000 + m_inc_addr), 76 | .ibyte (BYTE_4), 77 | .len (LEN_4), 78 | .burst (INCR), 79 | .id (1 + m_inc_id), 80 | .addr_delay (20), 81 | .data_delay (0) 82 | ); 83 | 84 | start_item(m_rd_seq); 85 | finish_item(m_rd_seq); 86 | 87 | m_inc_addr += 32'h0001_0000; 88 | m_inc_data += 32'h0001_0000; 89 | m_inc_id += 1; 90 | end 91 | 92 | endtask: sent_read_trx 93 | 94 | 95 | virtual task body(); 96 | 97 | `uvm_info(get_type_name(), "Starting...", UVM_MEDIUM) 98 | 99 | sent_write_trx(); 100 | sent_read_trx(); 101 | endtask 102 | 103 | endclass : DEMO_AXI_master_write_data_after_write_addr 104 | 105 | 106 | //-------------------------------------------- 107 | // SEQUENCE: DEMO_AXI_master_write_addr_after_write_data 108 | //-------------------------------------------- 109 | class DEMO_AXI_master_write_addr_after_write_data extends AXI_master_base_seq; 110 | 111 | AXI_master_write_seq m_wr_seq; 112 | AXI_master_read_seq m_rd_seq; 113 | 114 | int unsigned m_inc_addr = 0; 115 | int unsigned m_inc_data = 0; 116 | int unsigned m_inc_id = 0; 117 | 118 | `uvm_object_utils(DEMO_AXI_master_write_addr_after_write_data) 119 | 120 | function new(string name="axi_master_write_addr_after_write_data"); 121 | super.new(name); 122 | endfunction 123 | 124 | 125 | virtual task sent_write_trx(); 126 | 127 | m_inc_addr = 0; 128 | m_inc_data = 0; 129 | m_inc_id = 0; 130 | 131 | repeat(20) begin 132 | 133 | m_wr_seq = new ( 134 | .name ("axi_master_write_data_after_write_addr"), 135 | .addr (32'h0000_1000 + m_inc_addr), 136 | .ibyte (BYTE_4), 137 | .len (LEN_4), 138 | .burst (INCR), 139 | .id (1 + m_inc_id), 140 | .data ({ 32'h0000_0001 + m_inc_data, 141 | 32'h0000_0002 + m_inc_data, 142 | 32'h0000_0003 + m_inc_data, 143 | 32'h0000_0004 + m_inc_data} ), 144 | .strb ({4'b1111, 145 | 4'b1111, 146 | 4'b1111, 147 | 4'b1111 } ), 148 | .addr_delay (5),// write addr after write data 149 | .data_delay (0), 150 | .resp_delay (0) 151 | ); 152 | 153 | start_item(m_wr_seq); 154 | finish_item(m_wr_seq); 155 | 156 | m_inc_addr += 32'h0001_0000; 157 | m_inc_data += 32'h0001_0000; 158 | m_inc_id += 1; 159 | end 160 | 161 | endtask : sent_write_trx 162 | 163 | 164 | virtual task sent_read_trx(); 165 | 166 | m_inc_addr = 0; 167 | m_inc_data = 0; 168 | m_inc_id = 0; 169 | 170 | repeat(20) begin 171 | 172 | m_rd_seq = new( 173 | .name ("axi_master_write_data_after_write_addr"), 174 | .addr (32'h0000_1000 + m_inc_addr), 175 | .ibyte (BYTE_4), 176 | .len (LEN_4), 177 | .burst (INCR), 178 | .id (1 + m_inc_id), 179 | .addr_delay (20), 180 | .data_delay (0) 181 | ); 182 | 183 | start_item(m_rd_seq); 184 | finish_item(m_rd_seq); 185 | 186 | m_inc_addr += 32'h0001_0000; 187 | m_inc_data += 32'h0001_0000; 188 | m_inc_id += 1; 189 | end 190 | 191 | endtask: sent_read_trx 192 | 193 | 194 | virtual task body(); 195 | 196 | `uvm_info(get_type_name(), "Starting...", UVM_MEDIUM) 197 | 198 | sent_write_trx(); 199 | sent_read_trx(); 200 | 201 | endtask 202 | 203 | endclass : DEMO_AXI_master_write_addr_after_write_data 204 | 205 | 206 | `endif // DEMO_AXI_MASTER_SEQ_LIB_SV 207 | -------------------------------------------------------------------------------- /examples/virtual_master_to_dut_slave/demo_axi_slave_seq_lib.sv: -------------------------------------------------------------------------------- 1 | 2 | `ifndef DEMO_AXI_SLAVE_SEQ_LIB_SV 3 | `define DEMO_AXI_SLAVE_SEQ_LIB_SV 4 | 5 | class DEMO_AXI_slave_monitor extends AXI_slave_base_seq; 6 | 7 | function new(string name="demo_axi_slave_monitor"); 8 | super.new(name); 9 | `uvm_info(get_type_name(), "xxxxxxxx", UVM_MEDIUM) 10 | endfunction 11 | 12 | `uvm_object_utils_begin(DEMO_AXI_slave_monitor) 13 | `uvm_object_utils_end 14 | 15 | virtual task body(); 16 | `uvm_info(get_type_name(), "Starting...", UVM_MEDIUM) 17 | endtask 18 | 19 | endclass : DEMO_AXI_slave_monitor 20 | 21 | `endif // DEMO_AXI_SLAVE_SEQ_LIB_SV 22 | 23 | -------------------------------------------------------------------------------- /examples/virtual_master_to_dut_slave/demo_conf.sv: -------------------------------------------------------------------------------- 1 | 2 | `ifndef DEMO_CONF_SV 3 | `define DEMO_CONF_SV 4 | 5 | class Demo_conf extends AXI_env_conf; 6 | 7 | `uvm_object_utils(Demo_conf) 8 | 9 | function new(string name = "demo_config"); 10 | super.new(name); 11 | add_slave(.name ("m_slaves[0]"), 12 | .start_addr (32'h0000_0000), 13 | .end_addr (32'hFFFF_FFFF), 14 | .is_active (UVM_PASSIVE)); 15 | 16 | add_master(.name ("m_masters[0]"), 17 | .is_active (UVM_ACTIVE)); 18 | endfunction 19 | 20 | endclass : Demo_conf 21 | 22 | `endif // DEMO_CONF_SV 23 | 24 | -------------------------------------------------------------------------------- /examples/virtual_master_to_dut_slave/demo_lib.sv: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Demo_base_test extends uvm_test; 4 | 5 | `uvm_component_utils_begin(Demo_base_test) 6 | `uvm_component_utils_end 7 | 8 | Demo_tb m_demo_tb; 9 | uvm_table_printer m_printer; 10 | 11 | function new(string name = "demo_base_test", uvm_component parent); 12 | super.new(name,parent); 13 | m_printer = new(); 14 | endfunction : new 15 | 16 | virtual function void build_phase(uvm_phase phase); 17 | super.build_phase(phase); 18 | m_demo_tb = Demo_tb::type_id::create("m_demo_tb", this); 19 | endfunction : build_phase 20 | 21 | virtual function void connect_phase(uvm_phase phase); 22 | endfunction : connect_phase 23 | 24 | task run_phase(uvm_phase phase); 25 | m_printer.knobs.depth = 5; 26 | this.print(m_printer); 27 | phase.phase_done.set_drain_time(this, 1000); 28 | endtask : run_phase 29 | 30 | endclass : Demo_base_test 31 | 32 | 33 | //---------------------------------------------- 34 | // TEST: Test_write_data_after_write_addr 35 | //---------------------------------------------- 36 | class Test_write_data_after_write_addr extends Demo_base_test; 37 | 38 | `uvm_component_utils(Test_write_data_after_write_addr) 39 | 40 | function new(string name = "Test_write_data_after_write_addr", uvm_component parent); 41 | super.new(name,parent); 42 | endfunction : new 43 | 44 | virtual function void build_phase(uvm_phase phase); 45 | uvm_config_db #(uvm_object_wrapper)::set(this, "m_demo_tb.m_axi_env.m_masters[0].m_sequencer.run_phase", 46 | "default_sequence", DEMO_AXI_master_write_data_after_write_addr::type_id::get()); 47 | 48 | // uvm_config_db #(uvm_object_wrapper)::set(this, "m_demo_tb.m_axi_env.m_slaves[0].m_sequencer.run_phase", 49 | // "default_sequence", DEMO_AXI_slave_monitor::type_id::get()); 50 | 51 | super.build_phase(phase); 52 | endfunction : build_phase 53 | 54 | endclass : Test_write_data_after_write_addr 55 | 56 | //------------------------------------------------ 57 | // TEST: Test_write_addr_after_write_data 58 | //------------------------------------------------ 59 | class Test_write_addr_after_write_data extends Demo_base_test; 60 | 61 | `uvm_component_utils(Test_write_addr_after_write_data) 62 | 63 | function new(string name = "Test_write_addr_after_write_data", uvm_component parent); 64 | super.new(name,parent); 65 | endfunction : new 66 | 67 | virtual function void build_phase(uvm_phase phase); 68 | uvm_config_db #(uvm_object_wrapper)::set(this, "m_demo_tb.m_axi_env.m_masters[0].m_sequencer.run_phase", 69 | "default_sequence", DEMO_AXI_master_write_addr_after_write_data::type_id::get()); 70 | 71 | uvm_config_db #(uvm_object_wrapper)::set(this, "m_demo_tb.m_axi_env.m_slaves[0].m_sequencer.run_phase", 72 | "default_sequence", DEMO_AXI_slave_monitor::type_id::get()); 73 | 74 | super.build_phase(phase); 75 | endfunction : build_phase 76 | 77 | endclass : Test_write_addr_after_write_data 78 | 79 | 80 | -------------------------------------------------------------------------------- /examples/virtual_master_to_dut_slave/demo_pkg.sv: -------------------------------------------------------------------------------- 1 | 2 | `ifndef DEMO_PKG_SV 3 | `define DEMO_PKG_SV 4 | 5 | package demo_pkg; 6 | 7 | `include "axi_pkg.sv" 8 | `include "axi_seq_lib_pkg.sv" 9 | 10 | import uvm_pkg::*; 11 | `include "uvm_macros.svh" 12 | 13 | import axi_pkg::*; 14 | import axi_seq_lib_pkg::*; 15 | 16 | `include "demo_conf.sv" 17 | `include "demo_scoreboard.sv" 18 | `include "demo_axi_master_seq_lib.sv" 19 | `include "demo_axi_slave_seq_lib.sv" 20 | 21 | `include "demo_tb.sv" 22 | `include "demo_lib.sv" 23 | 24 | endpackage : demo_pkg 25 | 26 | `endif // DEMO_PKG_SV 27 | -------------------------------------------------------------------------------- /examples/virtual_master_to_dut_slave/demo_scoreboard.sv: -------------------------------------------------------------------------------- 1 | `ifndef DEMO_SCOREBOARD_SV 2 | `define DEMO_SCOREBOARD_SV 3 | 4 | `include "axi_transfer.sv" 5 | 6 | class Demo_scoreboard extends uvm_scoreboard; 7 | 8 | uvm_analysis_imp#(AXI_transfer, Demo_scoreboard) item_collected_imp; 9 | 10 | protected bit disable_scoreboard = 0; 11 | protected int m_num_writes = 0; 12 | protected int m_num_reads = 0; 13 | 14 | int unsigned m_mem_expected[int unsigned]; 15 | 16 | `uvm_component_utils_begin(Demo_scoreboard) 17 | `uvm_field_int (disable_scoreboard, UVM_ALL_ON) 18 | `uvm_field_int (m_num_writes, UVM_ALL_ON|UVM_DEC) 19 | `uvm_field_int (m_num_reads, UVM_ALL_ON|UVM_DEC) 20 | `uvm_component_utils_end 21 | 22 | // Constructor - required syntax for UVM automation and utilities 23 | function new (string name, uvm_component parent); 24 | super.new(name, parent); 25 | // Construct the TLM interface 26 | item_collected_imp = new("item_collected_imp", this); 27 | endfunction : new 28 | 29 | // Additional class methods 30 | extern virtual function void write(AXI_transfer trx); 31 | // extern virtual function void report(); 32 | 33 | extern virtual function void memory_verify(AXI_transfer trx); 34 | extern virtual function void connect_verify(AXI_transfer trx); 35 | 36 | endclass : Demo_scoreboard 37 | 38 | 39 | // TLM write() implementation 40 | function void Demo_scoreboard::write(AXI_transfer trx); 41 | 42 | if(!disable_scoreboard) begin 43 | `uvm_info(get_type_name(), $psprintf("Scoreboard \n%s", trx.sprint()), UVM_HIGH) 44 | 45 | memory_verify(trx); 46 | connect_verify(trx); 47 | // others ... 48 | end 49 | endfunction : write 50 | 51 | // connect verify 52 | function void Demo_scoreboard::connect_verify(AXI_transfer trx); 53 | endfunction : connect_verify 54 | 55 | // memory verify 56 | function void Demo_scoreboard::memory_verify(AXI_transfer trx); 57 | 58 | for (int unsigned i=0; i 2 | #include 3 | #include "vpi_user.h" 4 | #include "vpi_user_cds.h" 5 | 6 | //#define MEM_SIZE 8*1024*1024 7 | #define MEM_SIZE 40*1024*1024 8 | static unsigned int *p_base; 9 | 10 | void mem_alloc() { 11 | p_base = (unsigned int*) malloc(sizeof(unsigned int)*MEM_SIZE>>2); 12 | 13 | if (p_base==NULL) { 14 | vpi_printf("[FAIL]: can not allocate memory\n"); 15 | return; 16 | } else 17 | vpi_printf("[INFO]: allocate memory of %d byte successfully\n", MEM_SIZE); 18 | } 19 | 20 | void mem_free() { 21 | if (p_base==NULL) { 22 | vpi_printf("[FAIL]: memory pointer is wrong\n"); 23 | return; 24 | } else 25 | free(p_base); 26 | } 27 | 28 | void mem_read() { 29 | vpiHandle systf_hdl; 30 | vpiHandle arg_it; 31 | vpiHandle arg_hdl; 32 | 33 | s_vpi_value raddr; 34 | s_vpi_value rdata; 35 | 36 | systf_hdl = vpi_handle(vpiSysTfCall, NULL); 37 | 38 | if(!systf_hdl) { 39 | vpi_printf("[WARN]: No task handle available \n"); 40 | return; 41 | } 42 | 43 | arg_it = vpi_iterate(vpiArgument, systf_hdl); 44 | 45 | if(!arg_it) { 46 | vpi_printf("[FAIL]: No arguments for task mem_read\n"); 47 | return; 48 | } 49 | 50 | arg_hdl = vpi_scan(arg_it); 51 | 52 | raddr.format = vpiIntVal; 53 | rdata.format = vpiIntVal; 54 | 55 | vpi_get_value(arg_hdl, &raddr); 56 | //vpi_printf("[INFO]: raddr = %x\n ", raddr.value.integer); 57 | raddr.value.integer &= ~(0x03); 58 | raddr.value.integer >>= 2; 59 | 60 | 61 | if (raddr.value.integer>=MEM_SIZE) { 62 | vpi_printf("[FAIL]: raddr = %x exceed range\n ", raddr.value.integer); 63 | exit(1); 64 | } 65 | 66 | rdata.value.integer = *(p_base+raddr.value.integer); 67 | 68 | arg_hdl = vpi_scan(arg_it); 69 | vpi_put_value(arg_hdl, &rdata, NULL, vpiNoDelay); 70 | } 71 | 72 | void mem_write() { 73 | vpiHandle systf_hdl; 74 | vpiHandle arg_it; 75 | vpiHandle arg_hdl; 76 | 77 | s_vpi_value waddr; 78 | s_vpi_value we; 79 | s_vpi_value wdata; 80 | 81 | unsigned int temp; 82 | 83 | systf_hdl = vpi_handle(vpiSysTfCall, NULL); 84 | 85 | if(!systf_hdl) { 86 | vpi_printf("[WARN]: No task handle available \n"); 87 | return; 88 | } 89 | 90 | arg_it = vpi_iterate(vpiArgument, systf_hdl); 91 | 92 | if(!arg_it) { 93 | vpi_printf("[FAIL]: No arguments for task mem_write\n"); 94 | return; 95 | } 96 | 97 | arg_hdl = vpi_scan(arg_it); 98 | waddr.format = vpiIntVal; 99 | vpi_get_value(arg_hdl, &waddr); 100 | //vpi_printf("[INFO]: waddr = %x\n ", waddr.value.integer); 101 | 102 | if (waddr.value.integer>=MEM_SIZE) { 103 | vpi_printf("[FAIL]: waddr = %x exceed range\n ", waddr.value.integer); 104 | exit(1); 105 | } 106 | 107 | arg_hdl = vpi_scan(arg_it); 108 | we.format = vpiIntVal; 109 | vpi_get_value(arg_hdl, &we); 110 | //vpi_printf("[INFO]: we = %x\n ", we.value.integer); 111 | 112 | arg_hdl = vpi_scan(arg_it); 113 | wdata.format = vpiIntVal; 114 | vpi_get_value(arg_hdl, &wdata); 115 | //vpi_printf("[INFO]: wdata = %x\n ", wdata.value.integer); 116 | 117 | waddr.value.integer &= ~(0x03); 118 | waddr.value.integer >>= 2; 119 | 120 | temp = *(p_base+waddr.value.integer); 121 | 122 | if (we.value.integer&0x1) temp = (temp & ~(0x000000ff)) | (wdata.value.integer & 0x000000ff); 123 | if (we.value.integer&0x2) temp = (temp & ~(0x0000ff00)) | (wdata.value.integer & 0x0000ff00); 124 | if (we.value.integer&0x4) temp = (temp & ~(0x00ff0000)) | (wdata.value.integer & 0x00ff0000); 125 | if (we.value.integer&0x8) temp = (temp & ~(0xff000000)) | (wdata.value.integer & 0xff000000); 126 | 127 | *(p_base+waddr.value.integer) = temp; 128 | } 129 | 130 | void mem_init() { 131 | vpiHandle systf_hdl; 132 | vpiHandle arg_it; 133 | vpiHandle arg_hdl; 134 | 135 | s_vpi_value init_base; 136 | s_vpi_value init_name; 137 | 138 | int init_size; 139 | int temp; 140 | FILE *fp; 141 | 142 | systf_hdl = vpi_handle(vpiSysTfCall, NULL); 143 | 144 | if(!systf_hdl) { 145 | vpi_printf("[WARN]: No task handle available \n"); 146 | return; 147 | } 148 | 149 | arg_it = vpi_iterate(vpiArgument, systf_hdl); 150 | 151 | if(!arg_it) { 152 | vpi_printf("[FAIL]: No arguments for task mem_init\n"); 153 | return; 154 | } 155 | 156 | arg_hdl = vpi_scan(arg_it); 157 | 158 | init_base.format = vpiIntVal; 159 | init_name.format = vpiStringVal; 160 | 161 | vpi_get_value(arg_hdl, &init_base); 162 | vpi_printf("[INFO]: init_base = %x\n ", init_base.value.integer); 163 | init_base.value.integer &= ~(0x03); 164 | init_base.value.integer >>= 2; 165 | 166 | arg_hdl = vpi_scan(arg_it); 167 | vpi_get_value(arg_hdl, &init_name); 168 | vpi_printf("[INFO]: init_file = %s\n ", init_name.value.str); 169 | 170 | fp = fopen(init_name.value.str,"r"); 171 | 172 | if (!fp) 173 | printf("[FAIL]: can't open %s for init mem\n",init_name.value.str); 174 | 175 | fseek(fp,0,SEEK_END); 176 | init_size = ftell(fp); 177 | vpi_printf("[INFO]: init_size = %d\n ", init_size); 178 | 179 | // rewind to beginning 180 | fseek(fp,0,SEEK_SET); 181 | 182 | if (init_size>MEM_SIZE) { 183 | vpi_printf("[FAIL]: init_size is greater than memory size\n "); 184 | exit(1); 185 | } 186 | 187 | temp = fread(p_base+init_base.value.integer,1,init_size,fp); 188 | 189 | if (temp!=init_size) 190 | vpi_printf("[FAIL]: init file error\n"); 191 | else 192 | vpi_printf("[INFO]: init file ok\n"); 193 | 194 | fclose(fp); 195 | } 196 | 197 | void mem_dump() { 198 | vpiHandle systf_hdl; 199 | vpiHandle arg_it; 200 | vpiHandle arg_hdl; 201 | 202 | s_vpi_value dump_base; 203 | s_vpi_value dump_size; 204 | s_vpi_value dump_name; 205 | 206 | FILE *fp; 207 | 208 | systf_hdl = vpi_handle(vpiSysTfCall, NULL); 209 | 210 | if(!systf_hdl) { 211 | vpi_printf("[WARN]: No task handle available \n"); 212 | return; 213 | } 214 | 215 | arg_it = vpi_iterate(vpiArgument, systf_hdl); 216 | 217 | if(!arg_it) { 218 | vpi_printf("[FAIL]: No arguments for task mem_dump\n"); 219 | return; 220 | } 221 | 222 | arg_hdl = vpi_scan(arg_it); 223 | 224 | dump_base.format = vpiIntVal; 225 | dump_size.format = vpiIntVal; 226 | dump_name.format = vpiStringVal; 227 | 228 | vpi_get_value(arg_hdl, &dump_base); 229 | //vpi_printf("[INFO]: dump_base = %x\n ", dump_base.value.integer); 230 | dump_base.value.integer &= ~(0x03); 231 | dump_base.value.integer >>= 2; 232 | 233 | arg_hdl = vpi_scan(arg_it); 234 | vpi_get_value(arg_hdl, &dump_size); 235 | //vpi_printf("[INFO]: dump_size = %x\n ", dump_size.value.integer); 236 | 237 | if (dump_base.value.integer>=MEM_SIZE) { 238 | vpi_printf("[FAIL]: dump_base = %x exceed range\n ", dump_base.value.integer); 239 | exit(1); 240 | } 241 | 242 | if ((dump_base.value.integer+dump_size.value.integer)>=MEM_SIZE) { 243 | vpi_printf("[FAIL]: dump_base+dump_size exceed range\n "); 244 | exit(1); 245 | } 246 | 247 | arg_hdl = vpi_scan(arg_it); 248 | vpi_get_value(arg_hdl, &dump_name); 249 | 250 | vpi_printf("[INFO]: dump_file = %s\n ", dump_name.value.str); 251 | 252 | fp = fopen(dump_name.value.str,"wb"); 253 | 254 | if (!fp) 255 | printf("[FAIL]: can't open %s for dump mem\n",dump_name.value.str); 256 | 257 | fwrite(p_base+dump_base.value.integer,1,dump_size.value.integer,fp); 258 | 259 | fclose(fp); 260 | } 261 | 262 | // ----------------------------------------------------------------------------------------------------------------------------- 263 | void register_user_tasks() { 264 | s_vpi_systf_data task_data_s; 265 | 266 | p_vpi_systf_data task_data_p = &task_data_s; 267 | 268 | //------------------------------------------ 269 | task_data_p->type = vpiSysTask; 270 | task_data_p->tfname = "$mem_alloc"; 271 | task_data_p->calltf = (int(*)()) mem_alloc; 272 | task_data_p->compiletf = NULL; 273 | 274 | vpi_register_systf(task_data_p); 275 | //------------------------------------------ 276 | task_data_p->type = vpiSysTask; 277 | task_data_p->tfname = "$mem_free"; 278 | task_data_p->calltf = (int(*)()) mem_free; 279 | task_data_p->compiletf = NULL; 280 | 281 | vpi_register_systf(task_data_p); 282 | //------------------------------------------ 283 | task_data_p->type = vpiSysTask; 284 | task_data_p->tfname = "$mem_read"; 285 | task_data_p->calltf = (int(*)()) mem_read; 286 | task_data_p->compiletf = NULL; 287 | 288 | vpi_register_systf(task_data_p); 289 | //------------------------------------------ 290 | task_data_p->type = vpiSysTask; 291 | task_data_p->tfname = "$mem_write"; 292 | task_data_p->calltf = (int(*)()) mem_write; 293 | task_data_p->compiletf = NULL; 294 | 295 | vpi_register_systf(task_data_p); 296 | //------------------------------------------ 297 | task_data_p->type = vpiSysTask; 298 | task_data_p->tfname = "$mem_dump"; 299 | task_data_p->calltf = (int(*)()) mem_dump; 300 | task_data_p->compiletf = NULL; 301 | 302 | vpi_register_systf(task_data_p); 303 | //------------------------------------------ 304 | task_data_p->type = vpiSysTask; 305 | task_data_p->tfname = "$mem_init"; 306 | task_data_p->calltf = (int(*)()) mem_init; 307 | task_data_p->compiletf = NULL; 308 | 309 | vpi_register_systf(task_data_p); 310 | //------------------------------------------ 311 | } 312 | 313 | -------------------------------------------------------------------------------- /pli/vpi_user.c: -------------------------------------------------------------------------------- 1 | /* 2 | * |-----------------------------------------------------------------------| 3 | * | | 4 | * | Copyright Cadence Design Systems, Inc. 1985, 1988. | 5 | * | All Rights Reserved. Licensed Software. | 6 | * | | 7 | * | | 8 | * | THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF CADENCE DESIGN SYSTEMS | 9 | * | The copyright notice above does not evidence any actual or intended | 10 | * | publication of such source code. | 11 | * | | 12 | * |-----------------------------------------------------------------------| 13 | */ 14 | 15 | /* 16 | * |-------------------------------------------------------------| 17 | * | | 18 | * | PROPRIETARY INFORMATION, PROPERTY OF CADENCE DESIGN SYSTEMS | 19 | * | | 20 | * |-------------------------------------------------------------| 21 | */ 22 | 23 | #include 24 | #include "vpi_user.h" 25 | #include "vpi_user_cds.h" 26 | 27 | 28 | /* extern void setup_test_callbacks();*/ 29 | 30 | /* ---------------------------------------------------------------- 31 | The following is an example of what should be included in this file: 32 | 33 | extern void setup_my_callbacks(); <-- Add a declaration for your routine. 34 | 35 | void (*vlog_startup_routines[])() = 36 | { 37 | $*** add user entries here ***$ 38 | 39 | setup_my_callbacks, <-- Add your routine to the table. 40 | 41 | 0 $*** final entry must be 0 ***$ 42 | 43 | }; 44 | ------------------------------------------------------------------ */ 45 | extern void register_user_tasks(); 46 | 47 | void (*vlog_startup_routines[VPI_MAXARRAY])() = 48 | { 49 | register_user_tasks, 50 | 0 /*** final entry must be 0 ***/ 51 | }; 52 | 53 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | import unittest 4 | import os 5 | import re 6 | import subprocess 7 | 8 | DEBUG = True 9 | 10 | re_UVM_ERROR = re.compile("^UVM_ERROR\s+:\s+(\d+)$" ,re.M) 11 | re_UVM_WARNING = re.compile("^UVM_WARNING\s+:\s+(\d+)$" ,re.M) 12 | re_UVM_INFO = re.compile("^UVM_INFO\s+:\s+(\d+)$" ,re.M) 13 | re_IUS_ERROR = re.compile("^irun:\s+\*E\," ,re.M) 14 | re_IUS_WARNING = re.compile("^irun:\s+\*W\," ,re.M) 15 | 16 | class IUSAXIBase(unittest.TestCase): 17 | 18 | def setUp(self): 19 | os.chdir('/user/seanchen/project/uvm_axi') 20 | self.m_demo_home = os.getcwd() 21 | self.m_tb_home = None 22 | self.m_tb_name = None 23 | 24 | def tearDown(self): 25 | global DEBUG 26 | if not DEBUG: 27 | self.clearIUS(); 28 | 29 | def runIUS(self): 30 | """ run IUS ps: it need subprocess to parallel run """ 31 | 32 | m_str = "irun -uvm \ 33 | -incdir %(DEMO_HOME)s/sv \ 34 | -incdir %(DEMO_HOME)s/sv/sequence_libs \ 35 | -incdir %(DEMO_HOME)s/v \ 36 | -incdir %(DEMO_HOME)s/pli \ 37 | -incdir %(DEMO_HOME)s/dpi \ 38 | -incdir %(TB_HOME)s \ 39 | +notimingchecks \ 40 | +notchkmsg \ 41 | +no_notifier \ 42 | +define+AXI_BUSY \ 43 | +libext+.v \ 44 | %(DEMO_HOME)s/pli/dram.c \ 45 | %(DEMO_HOME)s/pli/vpi_user.c \ 46 | -loadvpi mem_alloc,mem_free,mem_read,mem_write,mem_init,mem_dump.register_user_tasks \ 47 | -coverage b:u \ 48 | -covoverwrite \ 49 | +UVM_TESTNAME=%(TEST_NAME)s \ 50 | +UVM_VERBOSITY=UVM_FULL \ 51 | %(TB_HOME)s/demo_top.sv \ 52 | +access+rwc" % { 'DEMO_HOME' : self.m_demo_home, \ 53 | 'TEST_NAME' : self.m_tb_name, \ 54 | 'TB_HOME' : self.m_tb_home} 55 | 56 | subprocess.call(m_str, shell=True) 57 | 58 | def reportIUS(self): 59 | """ report IUS """ 60 | # it need subprocess to return the sysout result 61 | # >>> grep "*E" irun.log 62 | # >>> grep "UVM_ERROR" irun.log ... 63 | 64 | f = open('./log/%(TEST_NAME)s.log' % { 'TEST_NAME' : self.m_tb_name}) 65 | uvm_errs = re_UVM_ERROR.findall(f.read()) 66 | ius_errs = re_IUS_ERROR.findall(f.read()) 67 | uvm_warns = re_UVM_WARNING.findall(f.read()) 68 | ius_warns = re_IUS_WARNING.findall(f.read()) 69 | 70 | if len(ius_errs) == 0: 71 | if uvm_errs[0] == '0': 72 | return True 73 | return False 74 | 75 | def clearIUS(self): 76 | """ clear """ 77 | os.system("rm -rf cov_work") 78 | os.system("rm -rf INCA_libs") 79 | os.system("rm -rf *.vcd*") 80 | 81 | 82 | def logIUS(self): 83 | """ move to log """ 84 | m_str = 'mv irun.log ./log/%(TEST_NAME)s.log' % { 'TEST_NAME' : self.m_tb_name} 85 | m_vcd = 'mv demo_tb.vcd ./log/%(TEST_NAME)s.vcd' % { 'TEST_NAME' : self.m_tb_name} 86 | os.system(m_str) 87 | os.system(m_vcd) 88 | 89 | 90 | class TestAXIBFMVirtual(IUSAXIBase): 91 | """ test virtual AXI bus functional model """ 92 | 93 | def test_phase_write_data_after_write_addr(self): 94 | """ test AXI write data phase is after write addr phase """ 95 | 96 | self.m_tb_home = self.m_demo_home + '/examples/virtual_master_to_virtual_slave' 97 | self.m_tb_name = 'Test_write_data_after_write_addr' 98 | 99 | self.runIUS() 100 | self.logIUS() 101 | self.assertEqual(self.reportIUS(), True) 102 | 103 | def test_phase_write_addr_after_write_data(self): 104 | """ test AXI write addr phase is after write data phase """ 105 | 106 | self.m_tb_home = self.m_demo_home + '/examples/virtual_master_to_virtual_slave' 107 | self.m_tb_name = 'Test_write_addr_after_write_data' 108 | 109 | self.runIUS() 110 | self.logIUS() 111 | self.assertEqual(self.reportIUS(), True) 112 | 113 | 114 | def test_phase_wite_addr_write_data_at_same_time(self): 115 | """ test AXI write addr and write data at the same time """ 116 | 117 | self.m_tb_home = self.m_demo_home + '/examples/virtual_master_to_virtual_slave' 118 | self.m_tb_name = 'Test_write_addr_write_data_at_same_time' 119 | 120 | self.runIUS() 121 | self.logIUS() 122 | self.assertEqual(self.reportIUS(), True) 123 | 124 | 125 | def test_phase_write_interleave_data(self): 126 | """ test AXI write interleave data """ 127 | 128 | self.m_tb_home = self.m_demo_home + '/examples/virtual_master_to_virtual_slave' 129 | self.m_tb_name = 'Test_write_interleave_data' 130 | 131 | self.runIUS() 132 | self.logIUS() 133 | self.assertEqual(self.reportIUS(), True) 134 | 135 | 136 | def test_phase_write_out_of_performance(self): 137 | """ test AXI write out of performance """ 138 | 139 | self.m_tb_home = self.m_demo_home + '/examples/virtual_master_to_virtual_slave' 140 | self.m_tb_name = 'Test_write_out_of_performance' 141 | 142 | self.runIUS() 143 | self.logIUS() 144 | self.assertEqual(self.reportIUS(), False) 145 | 146 | 147 | def test_phase_read_out_of_performance(self): 148 | """ test AXI read out of performance """ 149 | 150 | self.m_tb_home = self.m_demo_home + '/examples/virtual_master_to_virtual_slave' 151 | self.m_tb_name = 'Test_read_out_of_performance' 152 | 153 | self.runIUS() 154 | self.logIUS() 155 | self.assertEqual(self.reportIUS(), False) 156 | 157 | 158 | class TestAXIBFMDUT(IUSAXIBase): 159 | """ test AXI bus functional model """ 160 | 161 | def test_phase_write_data_after_write_addr(self): 162 | """ test AXI write data phase is after write addr phase """ 163 | 164 | self.m_tb_home = self.m_demo_home + '/examples/virtual_master_to_dut_slave' 165 | self.m_tb_name = 'Test_write_data_after_write_addr' 166 | 167 | self.runIUS() 168 | self.logIUS() 169 | self.assertEqual(self.reportIUS(), True) 170 | 171 | def test_phase_write_addr_after_write_data(self): 172 | """ test AXI write addr phase is after write data phase """ 173 | 174 | self.m_tb_home = self.m_demo_home + '/examples/virtual_master_to_dut_slave' 175 | self.m_tb_name = 'Test_write_addr_after_write_data' 176 | 177 | self.runIUS() 178 | self.logIUS() 179 | self.assertEqual(self.reportIUS(), True) 180 | 181 | 182 | def test_phase_wite_addr_write_data_at_same_time(self): 183 | """ test AXI write addr and write data at the same time """ 184 | 185 | self.m_tb_home = self.m_demo_home + '/examples/virtual_master_to_dut_slave' 186 | self.m_tb_name = 'Test_write_addr_write_data_at_same_time' 187 | 188 | self.runIUS() 189 | self.logIUS() 190 | self.assertEqual(self.reportIUS(), True) 191 | 192 | 193 | def test_phase_write_interleave_data(self): 194 | """ test AXI write interleave data """ 195 | 196 | self.m_tb_home = self.m_demo_home + '/examples/virtual_master_to_dut_slave' 197 | self.m_tb_name = 'Test_write_interleave_data' 198 | 199 | self.runIUS() 200 | self.logIUS() 201 | self.assertEqual(self.reportIUS(), True) 202 | 203 | 204 | def suite(): 205 | suite = unittest.TestSuite() 206 | 207 | suite.addTest(TestAXIBFMVirtual('test_phase_write_data_after_write_addr')) 208 | suite.addTest(TestAXIBFMVirtual('test_phase_write_addr_after_write_data')) 209 | suite.addTest(TestAXIBFMVirtual('test_phase_write_out_of_performance')) 210 | suite.addTest(TestAXIBFMVirtual('test_phase_read_out_of_performance')) 211 | 212 | suite.addTest(TestAXIBFMDUT('test_phase_write_data_after_write_addr')) 213 | suite.addTest(TestAXIBFMDUT('test_phase_write_addr_after_write_data')) 214 | 215 | return suite 216 | 217 | if __name__ == '__main__': 218 | runner = unittest.TextTestRunner() 219 | test_suite = suite() 220 | runner.run(test_suite) 221 | -------------------------------------------------------------------------------- /sv/axi_assertions.sv: -------------------------------------------------------------------------------- 1 | // define your assertions here .... 2 | 3 | -------------------------------------------------------------------------------- /sv/axi_common.sv: -------------------------------------------------------------------------------- 1 | /*-------------------------------------- 2 | // AXI common methods 3 | // file : axi_common.sv 4 | // author : SeanChen 5 | // date : 2013/05/06 6 | // description : common function calls, such queue, hash table .... 7 | ---------------------------------------*/ 8 | 9 | `ifndef AXI_COMMON_SV 10 | `define AXI_COMMON_SV 11 | 12 | /*------------------------------------- 13 | // hash map table [key(id):val(trx)] 14 | ---------------------------------------*/ 15 | class AXI_map extends uvm_object; 16 | 17 | int unsigned m_id; 18 | AXI_transfer m_trx; 19 | 20 | 21 | function new(string name="aix_map"); 22 | // super.new(name); 23 | endfunction 24 | 25 | endclass : AXI_map 26 | 27 | 28 | /*------------------------------------- 29 | // hash map Queue // or extends uvm_queue 30 | ---------------------------------------*/ 31 | class AXI_queue extends uvm_object; 32 | 33 | AXI_map m_queue[$]; 34 | AXI_transfer m_match_trx; 35 | int unsigned m_sort_queue[$]; 36 | 37 | function new(string name="aix_queue"); 38 | // super.new(name); 39 | endfunction 40 | 41 | extern virtual function bit fd_queued(int unsigned id); 42 | extern virtual function void en_queued(int unsigned id); 43 | extern virtual function void del_queued(int unsigned id); 44 | extern virtual function AXI_transfer peak_trx(); 45 | 46 | extern virtual function bit fd_sorted_queued(); 47 | extern virtual function AXI_transfer peak_completed_trx(); 48 | endclass : AXI_queue 49 | 50 | 51 | function bit AXI_queue::fd_sorted_queued(); 52 | int unsigned t_qos = 0; 53 | int index = -1; 54 | 55 | foreach (m_queue[i]) begin 56 | if (m_queue[i].m_trx.addr_done == `TRUE && 57 | m_queue[i].m_trx.data_done == `TRUE) begin 58 | if (m_queue[i].m_trx.qos <= t_qos) begin 59 | t_qos = m_queue[i].m_trx.qos; 60 | index = i; 61 | end 62 | end 63 | end 64 | 65 | if (index >= 0) begin 66 | m_match_trx = m_queue[index].m_trx; 67 | return `TRUE; 68 | end 69 | 70 | return `FALSE; 71 | endfunction : fd_sorted_queued 72 | 73 | 74 | function AXI_transfer AXI_queue::peak_completed_trx(); 75 | return m_match_trx; 76 | endfunction : peak_completed_trx 77 | 78 | 79 | function bit AXI_queue::fd_queued(int unsigned id); 80 | foreach(m_queue[i]) begin 81 | if (m_queue[i].m_id == id) begin 82 | m_match_trx = m_queue[i].m_trx; 83 | return `TRUE; 84 | end 85 | end 86 | return `FALSE; 87 | endfunction : fd_queued 88 | 89 | 90 | function void AXI_queue::en_queued(int unsigned id); 91 | AXI_transfer m_trx = new("axi_transfer"); 92 | AXI_map m_map = new("axi_map"); 93 | m_map.m_id = id; 94 | m_map.m_trx = m_trx; 95 | m_queue.push_back(m_map); 96 | m_match_trx = m_trx; 97 | endfunction : en_queued 98 | 99 | 100 | function void AXI_queue::del_queued(int unsigned id); 101 | int index = -1; 102 | foreach(m_queue[i]) begin 103 | if (m_queue[i].m_id == id) begin 104 | index = i; 105 | break; 106 | end 107 | end 108 | 109 | if (index >=0) begin 110 | // delete m_queue[index]; ??? how to del dynamic obj 111 | m_queue.delete(index); 112 | end 113 | 114 | endfunction : del_queued 115 | 116 | 117 | function AXI_transfer AXI_queue::peak_trx(); 118 | return m_match_trx; 119 | endfunction : peak_trx 120 | 121 | 122 | /*--------------------------------- 123 | // memory map table, addr caculate, and data byte map 124 | -----------------------------------*/ 125 | class AXI_mem_map extends uvm_object; 126 | 127 | int unsigned start_addr; 128 | int unsigned num_bytes; 129 | int unsigned data_bus_bytes; 130 | int unsigned aligned_addr; 131 | int unsigned burst_len; 132 | int unsigned addrs[$]; 133 | int unsigned datas[$]; 134 | int unsigned wrap_bound; 135 | bit aligned; 136 | int unsigned data_size; 137 | 138 | function new(string name="aix_queue"); 139 | // super.new(name); 140 | endfunction 141 | 142 | extern virtual function void run(AXI_transfer trx); 143 | extern virtual function void clear(); 144 | 145 | extern virtual function void map_start_addr(AXI_transfer trx); 146 | extern virtual function void map_num_bytes(AXI_transfer trx); 147 | extern virtual function void map_data_bus_bytes(AXI_transfer trx); 148 | extern virtual function void map_aligned_addr(AXI_transfer trx); 149 | extern virtual function void map_burst_len(AXI_transfer trx); 150 | extern virtual function void map_addrs(AXI_transfer trx); 151 | extern virtual function void map_datas(AXI_transfer trx); 152 | extern virtual function void map_wrap_bound(AXI_transfer trx); 153 | extern virtual function void map_data_size(AXI_transfer trx); 154 | 155 | endclass : AXI_mem_map 156 | 157 | 158 | function void AXI_mem_map::run(AXI_transfer trx); 159 | map_start_addr(trx); 160 | map_num_bytes(trx); 161 | map_data_bus_bytes(trx); 162 | map_aligned_addr(trx); 163 | map_burst_len(trx); 164 | map_data_size(trx); 165 | map_addrs(trx); 166 | map_datas(trx); 167 | map_wrap_bound(trx); 168 | endfunction : run 169 | 170 | 171 | function void AXI_mem_map::clear(); 172 | start_addr = 0; 173 | num_bytes = 0; 174 | data_bus_bytes = 0; 175 | aligned_addr = 0; 176 | burst_len = 0; 177 | addrs.delete(); 178 | datas.delete(); 179 | wrap_bound = 0; 180 | endfunction : clear 181 | 182 | // Start_Address 183 | function void AXI_mem_map::map_start_addr(AXI_transfer trx); 184 | start_addr = trx.addr; 185 | endfunction : map_start_addr 186 | 187 | // Number_Bytes 188 | function void AXI_mem_map::map_num_bytes(AXI_transfer trx); 189 | case(trx.size) 190 | BYTE_1 : num_bytes = 1; 191 | BYTE_2 : num_bytes = 2; 192 | BYTE_4 : num_bytes = 4; 193 | BYTE_8 : num_bytes = 8; 194 | BYTE_16 : num_bytes = 16; 195 | BYTE_32 : num_bytes = 32; 196 | BYTE_64 : num_bytes = 64; 197 | BYTE_128 : num_bytes = 128; 198 | default : `uvm_error("NOBYTE", {$psprintf("byte %d not support", trx.size)}) 199 | endcase 200 | endfunction : map_num_bytes 201 | 202 | //Data_Bus_Bytes 203 | function void AXI_mem_map::map_data_bus_bytes(AXI_transfer trx); 204 | data_bus_bytes = trx.data_bytes; 205 | endfunction : map_data_bus_bytes 206 | 207 | // Aligned_Address / Aligned 208 | function void AXI_mem_map::map_aligned_addr(AXI_transfer trx); 209 | aligned_addr = (start_addr / num_bytes) * num_bytes; 210 | aligned = (start_addr == aligned_addr); 211 | endfunction : map_aligned_addr 212 | 213 | // Burst_Length 214 | function void AXI_mem_map::map_burst_len(AXI_transfer trx); 215 | burst_len = (trx.len+1); 216 | endfunction : map_burst_len 217 | 218 | // Data Size 219 | function void AXI_mem_map::map_data_size(AXI_transfer trx); 220 | data_size = num_bytes * burst_len; 221 | endfunction : map_data_size 222 | 223 | // Address_N 224 | function void AXI_mem_map::map_addrs(AXI_transfer trx); 225 | 226 | int unsigned lower_wrap_boundary = 0; 227 | int unsigned upper_wrap_boundary = 0; 228 | int unsigned lower_byte_lane = 0; 229 | int unsigned upper_byte_lane = 0; 230 | int unsigned t_addr = start_addr; 231 | 232 | if (trx.burst == FIXED || trx.burst == RESERVED_BURST) 233 | `uvm_error("NOBURST", {"burst FIXED | RESERVED_BURST not supported"}) 234 | 235 | if (trx.burst == WRAP) begin 236 | lower_wrap_boundary = (t_addr/data_size) * data_size; 237 | upper_wrap_boundary = lower_wrap_boundary + data_size; 238 | end 239 | 240 | for (int i=1; i<=burst_len; i++) begin 241 | lower_byte_lane = t_addr - (t_addr/data_bus_bytes) * data_bus_bytes; 242 | 243 | if (aligned) 244 | upper_byte_lane = lower_byte_lane + num_bytes - 1; 245 | else 246 | upper_byte_lane = aligned_addr + num_bytes - 1 -(t_addr/data_bus_bytes)*data_bus_bytes; 247 | 248 | addrs.push_back(t_addr); 249 | 250 | if (trx.burst != FIXED) begin 251 | if (aligned) begin 252 | t_addr = t_addr + num_bytes; 253 | 254 | if (trx.burst == WRAP) begin 255 | if (t_addr >= upper_wrap_boundary) 256 | t_addr = lower_wrap_boundary; 257 | 258 | end else begin 259 | t_addr = t_addr + num_bytes; 260 | aligned = `TRUE; 261 | end 262 | end 263 | end 264 | end 265 | 266 | endfunction : map_addrs 267 | 268 | // Datas_N 269 | function void AXI_mem_map::map_datas(AXI_transfer trx); 270 | endfunction : map_datas 271 | 272 | // Wrap_Boundary 273 | function void AXI_mem_map::map_wrap_bound(AXI_transfer trx); 274 | wrap_bound = (start_addr / (num_bytes * burst_len)) * (num_bytes * burst_len); 275 | endfunction : map_wrap_bound 276 | 277 | 278 | `endif // AXI_COMMON_SV 279 | 280 | 281 | -------------------------------------------------------------------------------- /sv/axi_conf.sv: -------------------------------------------------------------------------------- 1 | 2 | /*-------------------------------------- 3 | // AXI configure 4 | // file : axi_conf.sv 5 | // author : SeanChen 6 | // date : 2013/05/06 7 | // description : configure file for master, slave, transfer .... 8 | ---------------------------------------*/ 9 | 10 | `ifndef AXI_CONF_SV 11 | `define AXI_CONF_SV 12 | 13 | /*------------------------------ 14 | base conf 15 | -------------------------------*/ 16 | class Base_conf extends uvm_object; 17 | int unsigned addr_wt_delay = 0; 18 | int unsigned data_wt_delay = 0; 19 | int unsigned resp_wt_delay = 0; 20 | int unsigned addr_rd_delay = 0; 21 | int unsigned data_rd_delay = 0; 22 | int unsigned half_cycle = 0; 23 | int unsigned tolerate_wt_delay = 500; 24 | int unsigned tolerate_rd_delay = 500; 25 | logic [0:0] finish = `FALSE; 26 | 27 | constraint c_tolerate_wt_delay { tolerate_wt_delay <= 1000; } 28 | constraint c_tolerate_rd_delay { tolerate_rd_delay <= 1000; } 29 | 30 | `uvm_object_utils_begin(Base_conf) 31 | `uvm_field_int(addr_wt_delay, UVM_DEFAULT) 32 | `uvm_field_int(data_wt_delay, UVM_DEFAULT) 33 | `uvm_field_int(resp_wt_delay, UVM_DEFAULT) 34 | `uvm_field_int(addr_rd_delay, UVM_DEFAULT) 35 | `uvm_field_int(data_rd_delay, UVM_DEFAULT) 36 | `uvm_field_int(half_cycle, UVM_DEFAULT) 37 | `uvm_field_int(tolerate_wt_delay, UVM_DEFAULT) 38 | `uvm_field_int(tolerate_rd_delay, UVM_DEFAULT) 39 | `uvm_field_int(finish, UVM_DEFAULT) 40 | `uvm_object_utils_end 41 | 42 | function new (string name = "axi_conf"); 43 | // super.new(name); 44 | endfunction 45 | 46 | endclass : Base_conf 47 | 48 | 49 | /*------------------------------ 50 | default AXI BUS parameter set 51 | -------------------------------*/ 52 | class AXI_conf extends Base_conf; 53 | 54 | int unsigned C_AXI_ID_WIDTH = 4; 55 | int unsigned C_AXI_ADDR_WIDTH = 32; 56 | int unsigned C_AXI_REG_WITH = 4; 57 | int unsigned C_AXI_DATA_WIDTH = 32; 58 | int unsigned C_AXI_LEN_WIDTH = 4; 59 | int unsigned C_AXI_SIZE_WIDTH = 3; 60 | int unsigned C_AXI_BURST_WIDTH = 2; 61 | int unsigned C_AXI_CACHE_WIDTH = 4; 62 | int unsigned C_AXI_PROT_WIDTH = 3; 63 | int unsigned C_AXI_QOS_WIDTH = 4; 64 | int unsigned C_AXI_STRB_WIDTH = 4; 65 | int unsigned C_AXI_RESP_WIDTH = 2; 66 | 67 | rand uvm_active_passive_enum is_active = UVM_ACTIVE; 68 | 69 | constraint cc_axi_id_width { C_AXI_ID_WIDTH <= 4; } 70 | constraint cc_axi_addr_width { C_AXI_ADDR_WIDTH <= 32; } 71 | constraint cc_axi_data_width { C_AXI_DATA_WIDTH <= 32; } 72 | constraint cc_axi_len_width { C_AXI_LEN_WIDTH <= 4; } 73 | constraint cc_axi_size_width { C_AXI_SIZE_WIDTH <= 3; } 74 | constraint cc_axi_burst_width { C_AXI_BURST_WIDTH <= 2; } 75 | constraint cc_axi_cache_width { C_AXI_CACHE_WIDTH <= 4; } 76 | constraint cc_axi_prot_width { C_AXI_PROT_WIDTH <= 3; } 77 | constraint cc_axi_qos_width { C_AXI_QOS_WIDTH <= 4; } 78 | constraint cc_axi_strb_width { C_AXI_STRB_WIDTH <= 4; } 79 | constraint cc_axi_resp_width { C_AXI_RESP_WIDTH <= 2; } 80 | 81 | `uvm_object_utils_begin(AXI_conf) 82 | `uvm_field_enum(uvm_active_passive_enum, is_active, UVM_DEFAULT) 83 | `uvm_field_int(C_AXI_ID_WIDTH, UVM_DEFAULT) 84 | `uvm_field_int(C_AXI_ADDR_WIDTH, UVM_DEFAULT) 85 | `uvm_field_int(C_AXI_DATA_WIDTH, UVM_DEFAULT) 86 | `uvm_field_int(C_AXI_LEN_WIDTH, UVM_DEFAULT) 87 | `uvm_field_int(C_AXI_SIZE_WIDTH, UVM_DEFAULT) 88 | `uvm_field_int(C_AXI_BURST_WIDTH, UVM_DEFAULT) 89 | `uvm_field_int(C_AXI_CACHE_WIDTH, UVM_DEFAULT) 90 | `uvm_field_int(C_AXI_PROT_WIDTH, UVM_DEFAULT) 91 | `uvm_field_int(C_AXI_QOS_WIDTH, UVM_DEFAULT) 92 | `uvm_field_int(C_AXI_STRB_WIDTH, UVM_DEFAULT) 93 | `uvm_field_int(C_AXI_RESP_WIDTH, UVM_DEFAULT) 94 | `uvm_object_utils_end 95 | 96 | function new (string name = "axi_conf"); 97 | // super.new(name); 98 | endfunction 99 | 100 | endclass : AXI_conf 101 | 102 | 103 | /*------------------------------- 104 | AXI master conf 105 | ---------------------------------*/ 106 | class AXI_master_conf extends AXI_conf; 107 | string name; 108 | 109 | rand master_type_enum mtype; 110 | 111 | `uvm_object_utils_begin(AXI_master_conf) 112 | `uvm_field_string(name, UVM_DEFAULT) 113 | `uvm_field_enum(master_type_enum, mtype, UVM_DEFAULT) 114 | `uvm_object_utils_end 115 | 116 | // Constructor - UVM required syntax 117 | function new (string name = "axi_master_conf"); 118 | super.new(name); 119 | endfunction 120 | 121 | endclass : AXI_master_conf 122 | 123 | 124 | /*----------------------------- 125 | AXI slave conf 126 | ------------------------------*/ 127 | class AXI_slave_conf extends AXI_conf; 128 | string name; 129 | int unsigned start_addr; 130 | int unsigned end_addr; 131 | int unsigned full_size; 132 | 133 | rand slave_type_enum stype; 134 | 135 | `uvm_object_utils_begin(AXI_slave_conf) 136 | `uvm_field_string(name, UVM_DEFAULT) 137 | `uvm_field_enum(slave_type_enum, stype, UVM_DEFAULT) 138 | `uvm_field_int(start_addr, UVM_DEFAULT) 139 | `uvm_field_int(end_addr, UVM_DEFAULT) 140 | `uvm_field_int(full_size, UVM_DEFAULT) 141 | `uvm_object_utils_end 142 | 143 | // Constructor - UVM required syntax 144 | function new (string name = "axi_master_conf"); 145 | super.new(name); 146 | endfunction 147 | 148 | endclass : AXI_slave_conf 149 | 150 | 151 | /*----------------------------------- 152 | AXI Env Configuration Information 153 | ------------------------------------*/ 154 | class AXI_env_conf extends uvm_object; 155 | 156 | AXI_master_conf m_master_confs[$]; 157 | AXI_slave_conf m_slave_confs[$]; 158 | 159 | int m_num_slaves; 160 | int m_num_masters; 161 | 162 | `uvm_object_utils_begin(AXI_env_conf) 163 | `uvm_field_queue_object(m_slave_confs, UVM_DEFAULT) 164 | `uvm_field_queue_object(m_master_confs, UVM_DEFAULT) 165 | `uvm_field_int(m_num_slaves, UVM_DEFAULT) 166 | `uvm_field_int(m_num_masters, UVM_DEFAULT) 167 | `uvm_object_utils_end 168 | 169 | function new (string name = "axi_env_conf"); 170 | super.new(name); 171 | endfunction 172 | 173 | extern function void add_slave(string name, 174 | int unsigned start_addr, 175 | int unsigned end_addr, 176 | int unsigned full_size = 5, 177 | int unsigned c_axi_id_width = 4, 178 | int unsigned c_axi_addr_width = 32, 179 | int unsigned c_axi_reg_width = 4, 180 | int unsigned c_axi_data_width = 32, 181 | int unsigned c_axi_len_width = 8, 182 | int unsigned c_axi_size_width = 3, 183 | int unsigned c_axi_burst_width = 2, 184 | int unsigned c_axi_cache_width = 4, 185 | int unsigned c_axi_prot_width = 3, 186 | int unsigned c_axi_qos_width = 4, 187 | int unsigned c_axi_strb_width = 4, 188 | int unsigned c_axi_resp_width = 2, 189 | uvm_active_passive_enum is_active = UVM_ACTIVE, 190 | slave_type_enum sv_type = VIRTUAL_SLAVE); 191 | 192 | extern function void add_master(string name, 193 | int unsigned c_axi_id_width = 4, 194 | int unsigned c_axi_addr_width = 32, 195 | int unsigned c_axi_reg_width = 4, 196 | int unsigned c_axi_data_width = 32, 197 | int unsigned c_axi_len_width = 8, 198 | int unsigned c_axi_size_width = 3, 199 | int unsigned c_axi_burst_width = 2, 200 | int unsigned c_axi_cache_width = 4, 201 | int unsigned c_axi_prot_width = 3, 202 | int unsigned c_axi_qos_width = 4, 203 | int unsigned c_axi_strb_width = 4, 204 | int unsigned c_axi_resp_width = 2, 205 | uvm_active_passive_enum is_active = UVM_ACTIVE, 206 | master_type_enum ms_type = VIRTUAL_MASTER); 207 | 208 | extern function AXI_master_conf get_master_by_name(string name); 209 | extern function AXI_slave_conf get_slave_by_name(string name); 210 | extern function AXI_slave_conf get_slave_by_addr(int unsigned addr); 211 | 212 | // extern virtual function void build_phase(uvm_phase phase); 213 | // extern virtual function void connect_phase(uvm_phase phase); 214 | 215 | extern virtual function void report_phase(uvm_phase phase); 216 | extern virtual function void report_masters(); 217 | extern virtual function void report_slaves(); 218 | 219 | endclass : AXI_env_conf 220 | 221 | 222 | function void AXI_env_conf::add_slave(string name, 223 | int unsigned start_addr, 224 | int unsigned end_addr, 225 | int unsigned full_size = 5, 226 | int unsigned c_axi_id_width = 4, 227 | int unsigned c_axi_addr_width = 32, 228 | int unsigned c_axi_reg_width = 4, 229 | int unsigned c_axi_data_width = 32, 230 | int unsigned c_axi_len_width = 8, 231 | int unsigned c_axi_size_width = 3, 232 | int unsigned c_axi_burst_width = 2, 233 | int unsigned c_axi_cache_width = 4, 234 | int unsigned c_axi_prot_width = 3, 235 | int unsigned c_axi_qos_width = 4, 236 | int unsigned c_axi_strb_width = 4, 237 | int unsigned c_axi_resp_width = 2, 238 | uvm_active_passive_enum is_active = UVM_ACTIVE, 239 | slave_type_enum sv_type = VIRTUAL_SLAVE); 240 | 241 | AXI_slave_conf t_slave_conf = AXI_slave_conf::type_id::create("axi_slave_conf"); 242 | t_slave_conf.name = name; 243 | t_slave_conf.start_addr = start_addr; 244 | t_slave_conf.end_addr = end_addr; 245 | t_slave_conf.full_size = full_size; 246 | t_slave_conf.is_active = is_active; 247 | t_slave_conf.stype = sv_type; 248 | 249 | t_slave_conf.C_AXI_ID_WIDTH = c_axi_id_width; 250 | t_slave_conf.C_AXI_ADDR_WIDTH = c_axi_addr_width; 251 | t_slave_conf.C_AXI_REG_WITH = c_axi_reg_width; 252 | t_slave_conf.C_AXI_DATA_WIDTH = c_axi_data_width; 253 | t_slave_conf.C_AXI_LEN_WIDTH = c_axi_len_width; 254 | t_slave_conf.C_AXI_SIZE_WIDTH = c_axi_size_width; 255 | t_slave_conf.C_AXI_BURST_WIDTH = c_axi_burst_width; 256 | t_slave_conf.C_AXI_CACHE_WIDTH = c_axi_cache_width; 257 | t_slave_conf.C_AXI_PROT_WIDTH = c_axi_prot_width; 258 | t_slave_conf.C_AXI_QOS_WIDTH = c_axi_qos_width; 259 | t_slave_conf.C_AXI_STRB_WIDTH = c_axi_strb_width; 260 | t_slave_conf.C_AXI_RESP_WIDTH = c_axi_resp_width; 261 | 262 | m_num_slaves++; 263 | m_slave_confs.push_back(t_slave_conf); 264 | endfunction : add_slave 265 | 266 | 267 | function void AXI_env_conf::add_master(string name, 268 | int unsigned c_axi_id_width = 4, 269 | int unsigned c_axi_addr_width = 32, 270 | int unsigned c_axi_reg_width = 4, 271 | int unsigned c_axi_data_width = 32, 272 | int unsigned c_axi_len_width = 8, 273 | int unsigned c_axi_size_width = 3, 274 | int unsigned c_axi_burst_width = 2, 275 | int unsigned c_axi_cache_width = 4, 276 | int unsigned c_axi_prot_width = 3, 277 | int unsigned c_axi_qos_width = 4, 278 | int unsigned c_axi_strb_width = 4, 279 | int unsigned c_axi_resp_width = 2, 280 | uvm_active_passive_enum is_active = UVM_ACTIVE, 281 | master_type_enum ms_type = VIRTUAL_MASTER); 282 | 283 | AXI_master_conf t_master_conf = AXI_master_conf::type_id::create("axi_master_conf"); 284 | t_master_conf.name = name; 285 | t_master_conf.is_active = is_active; 286 | t_master_conf.mtype = ms_type; 287 | 288 | t_master_conf.C_AXI_ID_WIDTH = c_axi_id_width; 289 | t_master_conf.C_AXI_ADDR_WIDTH = c_axi_addr_width; 290 | t_master_conf.C_AXI_REG_WITH = c_axi_reg_width; 291 | t_master_conf.C_AXI_DATA_WIDTH = c_axi_data_width; 292 | t_master_conf.C_AXI_LEN_WIDTH = c_axi_len_width; 293 | t_master_conf.C_AXI_SIZE_WIDTH = c_axi_size_width; 294 | t_master_conf.C_AXI_BURST_WIDTH = c_axi_burst_width; 295 | t_master_conf.C_AXI_CACHE_WIDTH = c_axi_cache_width; 296 | t_master_conf.C_AXI_PROT_WIDTH = c_axi_prot_width; 297 | t_master_conf.C_AXI_QOS_WIDTH = c_axi_qos_width; 298 | t_master_conf.C_AXI_STRB_WIDTH = c_axi_strb_width; 299 | t_master_conf.C_AXI_RESP_WIDTH = c_axi_resp_width; 300 | 301 | m_num_masters++; 302 | m_master_confs.push_back(t_master_conf); 303 | endfunction : add_master 304 | 305 | 306 | function AXI_master_conf AXI_env_conf::get_master_by_name(string name); 307 | foreach (m_master_confs[i]) begin 308 | if (m_master_confs[i].name == name) 309 | return m_master_confs[i]; 310 | end 311 | return null; 312 | endfunction : get_master_by_name 313 | 314 | 315 | function AXI_slave_conf AXI_env_conf::get_slave_by_name(string name); 316 | foreach (m_slave_confs[i]) begin 317 | if (m_slave_confs[i].name == name) 318 | return m_slave_confs[i]; 319 | end 320 | return null; 321 | endfunction : get_slave_by_name 322 | 323 | 324 | function AXI_slave_conf AXI_env_conf::get_slave_by_addr(int unsigned addr); 325 | foreach(m_slave_confs[i]) begin 326 | if (m_slave_confs[i].start_addr <= addr && 327 | m_slave_confs[i].end_addr >= addr) 328 | return m_slave_confs[i]; 329 | end 330 | return null; 331 | endfunction : get_slave_by_addr 332 | 333 | 334 | function void AXI_env_conf::report_masters(); 335 | foreach(m_master_confs[i]) begin 336 | //display 337 | end 338 | endfunction : report_masters 339 | 340 | 341 | function void AXI_env_conf::report_slaves(); 342 | foreach(m_slave_confs[i]) begin 343 | //display 344 | end 345 | endfunction : report_slaves 346 | 347 | 348 | function void AXI_env_conf::report_phase(uvm_phase phase); 349 | report_masters(); 350 | report_slaves(); 351 | endfunction : report_phase 352 | 353 | `endif // AXI_CONF_SV 354 | -------------------------------------------------------------------------------- /sv/axi_coverage.sv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/funningboy/uvm_axi/8b4dd7fd607a2a64de90ec8119c6eecd07903572/sv/axi_coverage.sv -------------------------------------------------------------------------------- /sv/axi_env.sv: -------------------------------------------------------------------------------- 1 | 2 | /*-------------------------------------- 3 | // AXI env 4 | // file : axi_env.sv 5 | // author : SeanChen 6 | // date : 2013/05/06 7 | // description : define how many master/slave and connection,,, 8 | ---------------------------------------*/ 9 | 10 | `ifndef AXI_ENV_SV 11 | `define AXI_EMV_SV 12 | 13 | class AXI_env extends uvm_env; 14 | 15 | AXI_env_conf m_env_conf; 16 | 17 | bit checks_enable = 1; 18 | bit coverage_enable = 1; 19 | 20 | AXI_master_agent m_masters[]; 21 | AXI_slave_agent m_slaves[]; 22 | 23 | `uvm_component_utils_begin(AXI_env) 24 | `uvm_field_object (m_env_conf, UVM_DEFAULT) 25 | `uvm_field_int (checks_enable, UVM_DEFAULT) 26 | `uvm_field_int (coverage_enable, UVM_DEFAULT) 27 | `uvm_field_array_object (m_masters, UVM_DEFAULT) 28 | `uvm_field_array_object (m_slaves, UVM_DEFAULT) 29 | `uvm_component_utils_end 30 | 31 | 32 | function new(string name, uvm_component parent); 33 | super.new(name, parent); 34 | endfunction : new 35 | 36 | 37 | extern virtual function void build_phase(uvm_phase phase); 38 | extern virtual function void start_of_simulation_phase(uvm_phase phase); 39 | 40 | extern virtual function void assign_conf(AXI_env_conf conf); 41 | 42 | extern virtual function void build_masters(); 43 | extern virtual function void build_slaves(); 44 | 45 | endclass : AXI_env 46 | 47 | 48 | function void AXI_env::build_phase(uvm_phase phase); 49 | super.build_phase(phase); 50 | if (m_env_conf == null) 51 | `uvm_error("NOCONF",{ "AXI ENV conf"} ) 52 | 53 | build_masters(); 54 | build_slaves(); 55 | assign_conf(m_env_conf); 56 | 57 | endfunction : build_phase 58 | 59 | 60 | function void AXI_env::build_masters(); 61 | 62 | m_masters = new[m_env_conf.m_master_confs.size()]; 63 | 64 | for(int i=0; i < m_env_conf.m_master_confs.size(); i++) begin 65 | m_masters[i] = AXI_master_agent::type_id::create($psprintf("m_masters[%0d]", i), this); 66 | m_masters[i].assign_conf(m_env_conf.m_master_confs[i]); 67 | end 68 | 69 | endfunction : build_masters 70 | 71 | 72 | function void AXI_env::build_slaves(); 73 | 74 | m_slaves = new[m_env_conf.m_slave_confs.size()]; 75 | 76 | for(int i=0; i < m_env_conf.m_slave_confs.size(); i++) begin 77 | m_slaves[i] = AXI_slave_agent::type_id::create($psprintf("m_slaves[%0d]", i), this); 78 | m_slaves[i].assign_conf(m_env_conf.m_slave_confs[i]); 79 | end 80 | 81 | endfunction : build_slaves 82 | 83 | 84 | // UVM start_of_simulation_phase 85 | function void AXI_env::start_of_simulation_phase(uvm_phase phase); 86 | set_report_id_action_hier("CFGOVR", UVM_DISPLAY); 87 | set_report_id_action_hier("CFGSET", UVM_DISPLAY); 88 | check_config_usage(); 89 | endfunction : start_of_simulation_phase 90 | 91 | 92 | function void AXI_env::assign_conf(AXI_env_conf conf); 93 | m_env_conf = conf; 94 | endfunction : assign_conf 95 | 96 | `endif // AXI_ENV_SV 97 | -------------------------------------------------------------------------------- /sv/axi_master_agent.sv: -------------------------------------------------------------------------------- 1 | /*-------------------------------------- 2 | // AXI master agent 3 | // file : axi_master_agent.sv 4 | // author : SeanChen 5 | // description : master agent, contain monitor or sequencer if the ACTIVE is on 6 | // date : 2013/05/06 7 | ---------------------------------------*/ 8 | 9 | `ifndef AXI_MASTER_AGENT_SV 10 | `define AXI_MASTER_AGENT_SV 11 | 12 | class AXI_master_agent extends uvm_agent; 13 | 14 | AXI_master_conf m_conf; 15 | virtual interface AXI_vif m_vif; 16 | 17 | AXI_master_driver m_driver; 18 | AXI_master_sequencer m_sequencer; 19 | AXI_master_monitor m_monitor; 20 | 21 | // reserve fields 22 | `uvm_component_utils_begin(AXI_master_agent) 23 | `uvm_component_utils_end 24 | 25 | 26 | // constructor 27 | function new(string name, uvm_component parent); 28 | super.new(name, parent); 29 | endfunction : new 30 | 31 | 32 | // build phase 33 | virtual function void build_phase(uvm_phase phase); 34 | super.build_phase(phase); 35 | 36 | m_monitor = AXI_master_monitor::type_id::create("m_monitor", this); 37 | 38 | if (m_conf.is_active == UVM_ACTIVE) begin 39 | m_driver = AXI_master_driver::type_id::create("m_driver", this); 40 | m_sequencer = AXI_master_sequencer::type_id::create("m_sequencer", this); 41 | end 42 | 43 | m_monitor.assign_conf(m_conf); 44 | 45 | if (m_conf.is_active == UVM_ACTIVE) begin 46 | // m_sequencer.assign_conf(axi_conf); 47 | m_driver.assign_conf(m_conf); 48 | end 49 | 50 | m_monitor.assign_vi(m_vif); 51 | if (m_conf.is_active == UVM_ACTIVE) begin 52 | // m_sequencer.assign_vi(m_vif); 53 | m_driver.assign_vi(m_vif); 54 | end 55 | 56 | endfunction : build_phase 57 | 58 | 59 | // connect phase 60 | virtual function void connect_phase(uvm_phase phase); 61 | if (m_conf.is_active == UVM_ACTIVE) begin 62 | m_driver.seq_item_port.connect(m_sequencer.seq_item_export); 63 | end 64 | endfunction : connect_phase 65 | 66 | 67 | // assign virtual interface 68 | virtual function void assign_vi(virtual interface AXI_vif axi_vif); 69 | m_vif = axi_vif; 70 | endfunction : assign_vi 71 | 72 | 73 | // assign configure 74 | virtual function void assign_conf(AXI_master_conf axi_conf); 75 | m_conf = axi_conf; 76 | endfunction : assign_conf 77 | 78 | endclass : AXI_master_agent 79 | 80 | 81 | `endif // AXI_master_agent 82 | 83 | -------------------------------------------------------------------------------- /sv/axi_master_driver.sv: -------------------------------------------------------------------------------- 1 | /*-------------------------------------- 2 | // AXI master driver 3 | // file : axi_master_driver.sv 4 | // author : SeanChen 5 | // date : 2013/05/06 6 | // brief : master driver, transfer TLM level info to pin level info 7 | ---------------------------------------*/ 8 | 9 | `ifndef AXI_MASTER_DRIVER_SV 10 | `define AXI_MASTER_DRIVER_SV 11 | 12 | class AXI_master_driver extends uvm_driver #(AXI_transfer); 13 | 14 | virtual interface AXI_vif m_vif; 15 | AXI_master_conf m_conf; 16 | AXI_transfer m_wr_queue[$]; 17 | AXI_transfer m_rd_queue[$]; 18 | int unsigned m_num_sent; 19 | 20 | event event_sent_write_trx; 21 | event event_sent_read_trx; 22 | 23 | int unsigned m_wr_addr_indx = 0; 24 | int unsigned m_wr_data_indx = 0; 25 | 26 | int unsigned m_rd_addr_indx = 0; 27 | 28 | // reserve fields 29 | `uvm_component_utils_begin(AXI_master_driver) 30 | `uvm_field_int (m_num_sent, UVM_ALL_ON) 31 | `uvm_component_utils_end 32 | 33 | // Constructor - required syntax for UVM automation and utilities 34 | function new (string name, uvm_component parent); 35 | super.new(name, parent); 36 | endfunction : new 37 | 38 | // Additional class methods 39 | extern virtual function void assign_vi(virtual interface AXI_vif vif); 40 | extern virtual function void assign_conf(AXI_master_conf conf); 41 | 42 | extern virtual task run_phase(uvm_phase phase); 43 | extern virtual function void connect_phase(uvm_phase phase); 44 | extern virtual protected task get_and_drive(); 45 | extern virtual protected task reset_signals(); 46 | extern virtual protected task drive_transfer(AXI_transfer trx); 47 | // extern virtual function void report(); 48 | 49 | extern virtual task sent_addr_write_trx(); 50 | extern virtual task sent_data_write_trx(); 51 | extern virtual task received_resp_write_trx(); 52 | 53 | extern virtual task sent_addr_read_trx(); 54 | extern virtual task received_data_read_trx(); 55 | 56 | extern virtual task free_write_trx(); 57 | extern virtual task free_read_trx(); 58 | 59 | extern virtual protected task wait_for_reset(); 60 | extern virtual protected task sent_trx_to_seq(); 61 | 62 | endclass : AXI_master_driver 63 | 64 | 65 | function void AXI_master_driver::assign_vi(virtual interface AXI_vif vif); 66 | m_vif = vif; 67 | endfunction 68 | 69 | 70 | function void AXI_master_driver::assign_conf(AXI_master_conf conf); 71 | m_conf = conf; 72 | endfunction 73 | 74 | 75 | //UVM connect_phase 76 | function void AXI_master_driver::connect_phase(uvm_phase phase); 77 | super.connect_phase(phase); 78 | 79 | if (!uvm_config_db#(virtual interface AXI_vif)::get(this, "", "m_vif", m_vif)) 80 | `uvm_error("NOVIF",{"virtual interface must be set for: ",get_full_name(),".m_vif"}) 81 | 82 | assert(m_conf!=null); 83 | // if (!uvm_config_db#(AXI_master_conf)::get(this, "", "m_conf", m_conf)) 84 | // `uvm_error("NOCONF",{"axi conf must be set for: ", get_full_name(), ".m_conf"}) 85 | 86 | endfunction : connect_phase 87 | 88 | 89 | // UVM run() phase spawn sub events 90 | task AXI_master_driver::run_phase(uvm_phase phase); 91 | fork 92 | get_and_drive(); 93 | reset_signals(); 94 | sent_addr_write_trx(); 95 | sent_data_write_trx(); 96 | received_resp_write_trx(); 97 | sent_addr_read_trx(); 98 | received_data_read_trx(); 99 | free_write_trx(); 100 | free_read_trx(); 101 | join 102 | endtask : run_phase 103 | 104 | 105 | // Gets transfers from the sequencer and passes them to the driver. 106 | task AXI_master_driver::get_and_drive(); 107 | wait_for_reset(); 108 | sent_trx_to_seq(); 109 | endtask : get_and_drive 110 | 111 | 112 | // Reset all master signals 113 | task AXI_master_driver::reset_signals(); 114 | forever begin 115 | @(posedge m_vif.AXI_ARESET_N); 116 | `uvm_info(get_type_name(), "Reset observed", UVM_MEDIUM) 117 | m_vif.AXI_AWID <= 0; 118 | m_vif.AXI_AWADDR <= 0; 119 | // .... 120 | end 121 | endtask : reset_signals 122 | 123 | 124 | task AXI_master_driver::wait_for_reset(); 125 | @(posedge m_vif.AXI_ARESET_N) 126 | `uvm_info(get_type_name(), "Reset dropped", UVM_MEDIUM) 127 | 128 | endtask : wait_for_reset 129 | 130 | 131 | // get next trx when reset has already done 132 | task AXI_master_driver::sent_trx_to_seq(); 133 | forever begin 134 | @(posedge m_vif.AXI_ACLK); 135 | seq_item_port.get_next_item(req); 136 | drive_transfer(req); 137 | seq_item_port.item_done(); 138 | end 139 | endtask : sent_trx_to_seq 140 | 141 | 142 | // free write trx 143 | task AXI_master_driver::free_write_trx(); 144 | 145 | endtask : free_write_trx 146 | 147 | // free read trx 148 | task AXI_master_driver::free_read_trx(); 149 | 150 | endtask : free_read_trx 151 | 152 | 153 | // Gets a transfer and drive it into the DUT 154 | // push the trx to trx async queue 155 | task AXI_master_driver::drive_transfer(AXI_transfer trx); 156 | 157 | `uvm_info(get_type_name(), $psprintf("Driving \n%s", trx.sprint()), UVM_HIGH) 158 | 159 | if (trx.rw == READ) begin 160 | m_rd_queue.push_back(trx); 161 | 162 | end else if (trx.rw == WRITE) begin 163 | m_wr_queue.push_back(trx); 164 | 165 | end else begin 166 | `uvm_error("NOTYPE",{"type not support"}) 167 | end 168 | 169 | m_num_sent++; 170 | `uvm_info(get_type_name(), $psprintf("Item %0d Sent ...", m_num_sent), UVM_HIGH) 171 | 172 | endtask : drive_transfer 173 | 174 | 175 | // addr write trx task by event_sent_write_trx.trigger 176 | task AXI_master_driver::sent_addr_write_trx(); 177 | AXI_transfer m_trx; 178 | 179 | forever begin 180 | // if write trx has existed... 181 | repeat(m_wr_queue.size()==0) @(posedge m_vif.AXI_ACLK); 182 | 183 | if (m_wr_addr_indx < m_wr_queue.size()) begin 184 | m_trx = m_wr_queue[m_wr_addr_indx]; 185 | 186 | repeat(m_trx.addr_wt_delay) @(posedge m_vif.AXI_ACLK); 187 | 188 | // sent trx 189 | `delay(m_conf.half_cycle); 190 | m_vif.AXI_AWVALID <= 1'b1; 191 | m_vif.AXI_AWID <= m_trx.id; 192 | m_vif.AXI_AWADDR <= m_trx.addr; 193 | m_vif.AXI_AWREG <= m_trx.region; 194 | m_vif.AXI_AWLEN <= m_trx.len; 195 | m_vif.AXI_AWSIZE <= m_trx.size; 196 | m_vif.AXI_AWBURST <= m_trx.burst; 197 | m_vif.AXI_AWLOCK <= m_trx.lock; 198 | m_vif.AXI_AWCACHE <= m_trx.cache; 199 | m_vif.AXI_AWPROT <= m_trx.prot; 200 | m_vif.AXI_AWQOS <= m_trx.qos; 201 | @(posedge m_vif.AXI_ACLK); 202 | 203 | // hold until AWREADY received 204 | while (!m_vif.AXI_AWREADY) @(posedge m_vif.AXI_ACLK); 205 | 206 | // free trx 207 | `delay(m_conf.half_cycle); 208 | m_vif.AXI_AWVALID <= 1'b0; 209 | m_trx.addr_done = `TRUE; 210 | @(posedge m_vif.AXI_ACLK); 211 | 212 | m_wr_addr_indx += 1; 213 | 214 | end else begin 215 | @(posedge m_vif.AXI_ACLK); 216 | end 217 | end 218 | 219 | endtask : sent_addr_write_trx 220 | 221 | 222 | // data write trx task by event_sent_write_trx.trigger 223 | task AXI_master_driver::sent_data_write_trx(); 224 | int unsigned i = 0; 225 | AXI_transfer m_trx; 226 | 227 | forever begin 228 | 229 | repeat(m_wr_queue.size()==0) @(posedge m_vif.AXI_ACLK); 230 | 231 | if (m_wr_data_indx < m_wr_queue.size()) begin 232 | m_trx = m_wr_queue[m_wr_data_indx]; 233 | 234 | repeat(m_trx.data_wt_delay) @(posedge m_vif.AXI_ACLK); 235 | 236 | // sent trx 237 | while (i<=m_trx.len) begin 238 | 239 | `delay(m_conf.half_cycle); 240 | m_vif.AXI_WVALID <= 1'b1; 241 | m_vif.AXI_WDATA <= m_trx.data[i]; 242 | m_vif.AXI_WSTRB <= m_trx.strb[i]; 243 | m_vif.AXI_WID <= m_trx.id; 244 | m_vif.AXI_WLAST <= (i==m_trx.len)? 1'b1 : 1'b0; 245 | @(posedge m_vif.AXI_ACLK); 246 | 247 | if (m_vif.AXI_WREADY && m_vif.AXI_WVALID) 248 | i = i+1; 249 | end 250 | 251 | // hold until all finish 252 | 253 | // free trx 254 | `delay(m_conf.half_cycle); 255 | m_vif.AXI_WVALID <= 1'b0; 256 | m_vif.AXI_WLAST <= 1'b0; 257 | i = 0; 258 | m_trx.data_done = `TRUE; 259 | @(posedge m_vif.AXI_ACLK); 260 | 261 | m_wr_data_indx += 1; 262 | 263 | end else begin 264 | @(posedge m_vif.AXI_ACLK); 265 | end 266 | end 267 | 268 | endtask : sent_data_write_trx 269 | 270 | 271 | // data resp trx collect resp to trx 272 | task AXI_master_driver::received_resp_write_trx(); 273 | 274 | forever begin 275 | `delay(m_conf.half_cycle); 276 | m_vif.AXI_BREADY <= 1'b0; 277 | repeat($urandom_range(4,8)) @(posedge m_vif.AXI_ACLK); 278 | 279 | `delay(m_conf.half_cycle); 280 | m_vif.AXI_BREADY <= 1'b1; 281 | @(posedge m_vif.AXI_ACLK); 282 | 283 | // hold until BVALID received 284 | while(!m_vif.AXI_BVALID) @(posedge m_vif.AXI_ACLK); 285 | end 286 | 287 | endtask : received_resp_write_trx 288 | 289 | 290 | // addr read trx 291 | task AXI_master_driver::sent_addr_read_trx(); 292 | AXI_transfer m_trx; 293 | 294 | forever begin 295 | 296 | repeat(m_rd_queue.size()==0) @(posedge m_vif.AXI_ACLK); 297 | 298 | if (m_rd_addr_indx < m_rd_queue.size()) begin 299 | m_trx = m_rd_queue[m_rd_addr_indx]; 300 | 301 | repeat(m_trx.addr_rd_delay) @(posedge m_vif.AXI_ACLK); 302 | 303 | // sent trx 304 | `delay(m_conf.half_cycle); 305 | m_vif.AXI_ARVALID <= 1'b1; 306 | m_vif.AXI_ARID <= m_trx.id; 307 | m_vif.AXI_ARADDR <= m_trx.addr; 308 | m_vif.AXI_ARREADY <= m_trx.region; 309 | m_vif.AXI_ARLEN <= m_trx.len; 310 | m_vif.AXI_ARSIZE <= m_trx.size; 311 | m_vif.AXI_ARBURST <= m_trx.burst; 312 | m_vif.AXI_ARLOCK <= m_trx.lock; 313 | m_vif.AXI_ARCACHE <= m_trx.cache; 314 | m_vif.AXI_ARPROT <= m_trx.prot; 315 | m_vif.AXI_ARQOS <= m_trx.qos; 316 | @(posedge m_vif.AXI_ACLK); 317 | 318 | // hold until ARREADY received 319 | while(!m_vif.AXI_ARREADY) @(posedge m_vif.AXI_ACLK); 320 | //void'(m_rd_queue.pop_front()); 321 | 322 | // free trx 323 | `delay(m_conf.half_cycle); 324 | m_vif.AXI_ARVALID <= 1'b0; 325 | @(posedge m_vif.AXI_ACLK); 326 | 327 | m_rd_addr_indx += 1; 328 | 329 | end else begin 330 | @(posedge m_vif.AXI_ACLK); 331 | end 332 | end 333 | 334 | endtask : sent_addr_read_trx 335 | 336 | 337 | // data read trx 338 | task AXI_master_driver::received_data_read_trx(); 339 | 340 | forever begin 341 | `delay(m_conf.half_cycle); 342 | m_vif.AXI_RREADY <= 1'b0; 343 | repeat($urandom_range(4,8)) @(posedge m_vif.AXI_ACLK); 344 | 345 | `delay(m_conf.half_cycle); 346 | m_vif.AXI_RREADY <= 1'b1; 347 | @(posedge m_vif.AXI_ACLK); 348 | 349 | // hold until RVALID received 350 | while(!m_vif.AXI_RVALID) @(posedge m_vif.AXI_ACLK); 351 | 352 | // continuous burst case 353 | `delay(m_conf.half_cycle); 354 | m_vif.AXI_RREADY <= 1'b1; 355 | repeat($urandom_range(4,16)) @(posedge m_vif.AXI_ACLK); 356 | end 357 | 358 | endtask : received_data_read_trx 359 | 360 | 361 | // finish read trx 362 | 363 | `endif // AXI_MASTER_DRIVER_SV 364 | 365 | 366 | -------------------------------------------------------------------------------- /sv/axi_master_monitor.sv: -------------------------------------------------------------------------------- 1 | /*-------------------------------------- 2 | // AXI master monitor 3 | // file : axi_master_monitor.sv 4 | // author : SeanChen 5 | // date : 2013/05/06 6 | // description : master watch monitor 7 | ---------------------------------------*/ 8 | 9 | `ifndef AXI_MASTER_MONITOR_SV 10 | `define AXI_MASTER_MONITOR_SV 11 | 12 | class AXI_master_monitor extends uvm_monitor; 13 | 14 | virtual interface AXI_vif m_vif; 15 | AXI_master_conf m_conf; 16 | 17 | // cycle count 18 | longint m_cycle = 0; 19 | 20 | // Count transfers collected 21 | int m_num_col; 22 | 23 | // trx stored queue 24 | AXI_queue m_wr_queue; 25 | AXI_queue m_rd_queue; 26 | 27 | // trx mem map table 28 | AXI_mem_map m_mem_map; 29 | 30 | // recorder 31 | integer m_file; 32 | 33 | // The following two bits are used to control whether checks and coverage are 34 | // done in the monitor 35 | bit checks_enable = 1; 36 | bit coverage_enable = 1; 37 | 38 | // The current AXI_transfer 39 | protected AXI_transfer trans_collected; 40 | 41 | // This TLM port is used to connect the monitor to the scoreboard 42 | uvm_analysis_port #(AXI_transfer) item_collected_port; 43 | 44 | /*-------------------------------- 45 | // Covergroup for transfer 46 | ----------------------------------*/ 47 | covergroup master_transfer_cg; 48 | TRANS_ADDR : coverpoint trans_collected.addr { 49 | bins ZERO = {0}; 50 | bins NON_ZERO = {[1:8'hff]}; 51 | } 52 | TRANS_DIRECTION : coverpoint trans_collected.rw { 53 | bins READ = {READ}; 54 | bins WRITE = {WRITE}; 55 | } 56 | TRANS_LEN : coverpoint trans_collected.len { 57 | 58 | } 59 | TRANS_SIZE : coverpoint trans_collected.size { 60 | } 61 | 62 | // TRANS_DATA : coverpoint trans_collected.data { 63 | // bins ZERO = {0}; 64 | // bins NON_ZERO = {[1:8'hff]}; 65 | // } 66 | TRANS_ADDR_X_TRANS_DIRECTION: cross TRANS_ADDR, TRANS_DIRECTION; 67 | endgroup : master_transfer_cg 68 | 69 | // Provide UVM automation and utility methods 70 | `uvm_component_utils_begin(AXI_master_monitor) 71 | `uvm_field_int (m_num_col, UVM_DEFAULT) 72 | `uvm_field_int (checks_enable, UVM_ALL_ON) 73 | `uvm_field_int (coverage_enable, UVM_ALL_ON) 74 | `uvm_component_utils_end 75 | 76 | 77 | // Constructor - required syntax for UVM automation and utilities 78 | function new (string name, uvm_component parent); 79 | super.new(name, parent); 80 | 81 | // Create the covergroup 82 | master_transfer_cg = new(); 83 | master_transfer_cg.set_inst_name("master_transfer_cg"); 84 | 85 | // Create the TLM port 86 | item_collected_port = new("item_collected_port", this); 87 | 88 | // wr/rd queue 89 | m_wr_queue = new(); 90 | m_rd_queue = new(); 91 | 92 | // mem map 93 | m_mem_map = new(); 94 | 95 | // 96 | m_file = $fopen({$psprintf("%h.trx", this)}, "w"); 97 | endfunction : new 98 | 99 | // Additional class methods 100 | extern virtual function void assign_vi(virtual interface AXI_vif vif); 101 | extern virtual function void assign_conf(AXI_master_conf conf); 102 | 103 | extern virtual task run_phase(uvm_phase phase); 104 | extern virtual function void connect_phase(uvm_phase phase); 105 | extern virtual protected task collect_write_transfer(); 106 | extern virtual protected task collect_read_transfer(); 107 | extern virtual protected task perform_write_checks(); 108 | extern virtual protected task perform_read_checks(); 109 | extern virtual protected task perform_write_coverage(); 110 | extern virtual protected task perform_read_coverage(); 111 | extern virtual function void report(); 112 | extern virtual protected function void dump_trx(AXI_transfer trx); 113 | extern virtual protected function void head_trx(); 114 | 115 | extern virtual protected task collect_addr_write_trx(); 116 | extern virtual protected task collect_data_write_trx(); 117 | extern virtual protected task collect_resp_write_trx(); 118 | extern virtual protected task collect_addr_read_trx(); 119 | extern virtual protected task collect_data_read_trx(); 120 | 121 | extern virtual protected task collect_cycle_count(); 122 | 123 | endclass : AXI_master_monitor 124 | 125 | 126 | function void AXI_master_monitor::assign_vi(virtual interface AXI_vif vif); 127 | m_vif = vif; 128 | endfunction : assign_vi 129 | 130 | 131 | function void AXI_master_monitor::assign_conf(AXI_master_conf conf); 132 | m_conf = conf; 133 | endfunction : assign_conf 134 | 135 | 136 | //UVM connect_phase 137 | function void AXI_master_monitor::connect_phase(uvm_phase phase); 138 | super.connect_phase(phase); 139 | 140 | if (!uvm_config_db#(virtual AXI_vif)::get(this, "", "m_vif", m_vif)) 141 | `uvm_error("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"}) 142 | 143 | assert(m_conf!=null); 144 | // if (!uvm_config_db#(AXI_master_conf)::get(this, "", "m_conf", m_conf)) 145 | // `uvm_error("NOCONF",{"axi conf must be set for: ", get_full_name(), ".m_conf"}) 146 | 147 | head_trx(); 148 | endfunction : connect_phase 149 | 150 | 151 | function void AXI_master_monitor::head_trx(); 152 | 153 | $fwrite(m_file, { 154 | $psprintf("# %s,\n", get_full_name())}); 155 | 156 | // buildup header for trx recorder 157 | $fwrite(m_file, { 158 | $psprintf("%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n", 159 | "begin_cycle", 160 | "end_cycle", 161 | "used_cycle", 162 | "begin_time", 163 | "end_time", 164 | "r/w", 165 | "addr", 166 | "len", 167 | "size", 168 | "burst", 169 | "datas",) 170 | }); 171 | 172 | endfunction : head_trx 173 | 174 | 175 | // UVM run() phase 176 | task AXI_master_monitor::run_phase(uvm_phase phase); 177 | fork 178 | collect_write_transfer(); 179 | collect_read_transfer(); 180 | collect_cycle_count(); 181 | join 182 | endtask : run_phase 183 | 184 | // dump trx as the same method as uvm_recorder 185 | // for 32 bits solution 186 | function void AXI_master_monitor::dump_trx(AXI_transfer trx); 187 | 188 | $fwrite(m_file, { 189 | $psprintf("%16h, %16h, %16h, %16h, %16h, %s, %8h, %8h, %8h, %8h, ", 190 | trx.begin_cycle, 191 | trx.end_cycle, 192 | trx.used_cycle, 193 | trx.begin_time, 194 | trx.end_time, 195 | (trx.rw == READ)? "R" : "W", 196 | trx.addr, 197 | trx.len, 198 | trx.size, 199 | trx.burst) 200 | }); 201 | 202 | foreach (trx.data[i]) begin 203 | $fwrite(m_file, { 204 | $psprintf("%8h(%8h), ", 205 | trx.data[i], 206 | trx.strb[i]) 207 | }); 208 | end 209 | 210 | $fwrite(m_file, "\n"); 211 | 212 | endfunction : dump_trx 213 | 214 | 215 | /*************************************************************************** 216 | IVB-NOTE : REQUIRED : master Monitor : Monitors 217 | ------------------------------------------------------------------------- 218 | Modify the collect_transfers() method to match your protocol. 219 | Note that if you change/add signals to the physical interface, you must 220 | also change this method. 221 | ***************************************************************************/ 222 | 223 | task AXI_master_monitor::collect_cycle_count(); 224 | forever begin 225 | @(posedge m_vif.AXI_ACLK); 226 | m_cycle += 1; 227 | end 228 | endtask : collect_cycle_count 229 | 230 | 231 | task AXI_master_monitor::collect_write_transfer(); 232 | fork 233 | collect_addr_write_trx(); 234 | collect_data_write_trx(); 235 | collect_resp_write_trx(); 236 | 237 | if (checks_enable) 238 | perform_write_checks(); 239 | if (coverage_enable) 240 | perform_write_coverage(); 241 | join 242 | endtask : collect_write_transfer 243 | 244 | 245 | task AXI_master_monitor::collect_read_transfer(); 246 | fork 247 | collect_addr_read_trx(); 248 | collect_data_read_trx(); 249 | 250 | if (checks_enable) 251 | perform_read_checks(); 252 | if (coverage_enable) 253 | perform_read_coverage(); 254 | join 255 | endtask : collect_read_transfer 256 | 257 | 258 | // collect addr write @ neg edge 259 | task AXI_master_monitor::collect_addr_write_trx(); 260 | AXI_transfer t_trx; 261 | 262 | // check write pool, if the W(id) is not found,then create a new one 263 | // and recored it's timing 264 | 265 | forever begin 266 | @(negedge m_vif.AXI_ACLK iff (m_vif.AXI_AWVALID == 1'b1 && 267 | m_vif.AXI_AWREADY == 1'b1)); 268 | 269 | if (!m_wr_queue.fd_queued(m_vif.AXI_AWID)) begin 270 | m_wr_queue.en_queued(m_vif.AXI_AWID); 271 | t_trx = m_wr_queue.peak_trx(); 272 | t_trx.begin_cycle = m_cycle; 273 | t_trx.begin_time = $time; 274 | void'(begin_tr(t_trx, {$psprintf("axi_master_monitor[%h]", this)})); 275 | end 276 | 277 | t_trx = m_wr_queue.peak_trx(); 278 | t_trx.rw = WRITE; 279 | t_trx.addr = dec_addr (m_vif.AXI_AWADDR); 280 | t_trx.id = dec_id (m_vif.AXI_AWID); 281 | t_trx.region = dec_region(m_vif.AXI_AWREG); 282 | t_trx.len = dec_len (m_vif.AXI_AWLEN); 283 | t_trx.size = dec_byte (m_vif.AXI_AWSIZE); 284 | t_trx.burst = dec_burst (m_vif.AXI_AWBURST); 285 | t_trx.lock = dec_lock (m_vif.AXI_AWLOCK); 286 | t_trx.cache = dec_cache (m_vif.AXI_AWCACHE); 287 | t_trx.prot = dec_prot (m_vif.AXI_AWPROT); 288 | t_trx.qos = dec_qos (m_vif.AXI_AWQOS); 289 | 290 | `uvm_info(get_type_name(), $psprintf("collected write addr addr : %h, len : %h", t_trx.addr, t_trx.len), UVM_LOW) 291 | 292 | // extend trx 293 | m_mem_map.run(t_trx); 294 | t_trx.mem_addrs = m_mem_map.addrs; 295 | t_trx.mem_datas = m_mem_map.datas; 296 | m_mem_map.clear(); 297 | 298 | t_trx.itype = MASTER; 299 | t_trx.master = get_full_name(); 300 | 301 | t_trx.addr_done = `TRUE; 302 | end 303 | 304 | endtask : collect_addr_write_trx 305 | 306 | 307 | // collect write data @ neg edge 308 | task AXI_master_monitor::collect_data_write_trx(); 309 | AXI_transfer t_trx; 310 | 311 | // check write pool, if the W(id) is not found, create a new one 312 | // and recored it's timing 313 | 314 | forever begin 315 | @(negedge m_vif.AXI_ACLK iff (m_vif.AXI_WVALID == 1'b1 && 316 | m_vif.AXI_WREADY == 1'b1)); 317 | 318 | if (!m_wr_queue.fd_queued(m_vif.AXI_WID)) begin 319 | m_wr_queue.en_queued(m_vif.AXI_WID); 320 | t_trx = m_wr_queue.peak_trx(); 321 | t_trx.begin_cycle = m_cycle; 322 | t_trx.begin_time = $time; 323 | void'(begin_tr(t_trx, {$psprintf("axi_master_monitor[%h]", this)})); 324 | end 325 | 326 | t_trx = m_wr_queue.peak_trx(); 327 | t_trx.data.push_back( dec_data(m_vif.AXI_WDATA) ); 328 | t_trx.strb.push_back( dec_strb(m_vif.AXI_WSTRB) ); 329 | 330 | t_trx.used_cycle = t_trx.used_cycle + 1; 331 | 332 | if (m_vif.AXI_WLAST == 1'b1) 333 | t_trx.data_done = `TRUE; 334 | 335 | end 336 | 337 | endtask : collect_data_write_trx 338 | 339 | 340 | // collect resp @ pos edge 341 | task AXI_master_monitor::collect_resp_write_trx(); 342 | AXI_transfer t_trx; 343 | 344 | forever begin 345 | @(posedge m_vif.AXI_ACLK iff (m_vif.AXI_BVALID == 1'b1 && 346 | m_vif.AXI_BREADY == 1'b1)); 347 | 348 | if (m_wr_queue.fd_queued(m_vif.AXI_BID)) begin 349 | t_trx = m_wr_queue.peak_trx(); 350 | 351 | if (t_trx.addr_done == `TRUE && 352 | t_trx.data_done == `TRUE) begin 353 | 354 | t_trx.resp = dec_resp (m_vif.AXI_BRESP); 355 | t_trx.end_cycle = m_cycle; 356 | t_trx.end_time = $time; 357 | void'(end_tr(t_trx)); 358 | 359 | // Send transfer to scoreboard via TLM write() 360 | item_collected_port.write(t_trx); 361 | dump_trx(t_trx); 362 | m_wr_queue.del_queued(m_vif.AXI_BID); 363 | end 364 | 365 | end else begin 366 | `uvm_error("NOIDMATCH", {$psprintf("BID %h is not found in write queue", m_vif.AXI_BID), get_full_name()}) 367 | end 368 | end 369 | 370 | endtask : collect_resp_write_trx 371 | 372 | 373 | // collect addr read @ neg edge 374 | task AXI_master_monitor::collect_addr_read_trx(); 375 | AXI_transfer t_trx; 376 | 377 | forever begin 378 | @(negedge m_vif.AXI_ACLK iff (m_vif.AXI_ARVALID == 1'b1 && 379 | m_vif.AXI_ARREADY == 1'b1)); 380 | 381 | if (!m_rd_queue.fd_queued(m_vif.AXI_ARID)) begin 382 | m_rd_queue.en_queued(m_vif.AXI_ARID); 383 | t_trx = m_rd_queue.peak_trx(); 384 | t_trx.begin_cycle = m_cycle; 385 | t_trx.begin_time = $time; 386 | void'(begin_tr(t_trx, {$psprintf("axi_master_monitor[%h]", this)})); 387 | end 388 | 389 | t_trx = m_rd_queue.peak_trx(); 390 | t_trx.rw = READ; 391 | t_trx.addr = dec_addr (m_vif.AXI_ARADDR); 392 | t_trx.id = dec_id (m_vif.AXI_ARID); 393 | t_trx.region = dec_region(m_vif.AXI_ARREG); 394 | t_trx.len = dec_len (m_vif.AXI_ARLEN); 395 | t_trx.size = dec_byte (m_vif.AXI_ARSIZE); 396 | t_trx.burst = dec_burst (m_vif.AXI_ARBURST); 397 | t_trx.lock = dec_lock (m_vif.AXI_ARLOCK); 398 | t_trx.cache = dec_cache (m_vif.AXI_ARCACHE); 399 | t_trx.prot = dec_prot (m_vif.AXI_ARPROT); 400 | t_trx.qos = dec_qos (m_vif.AXI_ARQOS); 401 | 402 | // extend trx 403 | m_mem_map.run(t_trx); 404 | t_trx.mem_addrs = m_mem_map.addrs; 405 | t_trx.mem_datas = m_mem_map.datas; 406 | m_mem_map.clear(); 407 | 408 | t_trx.itype = MASTER; 409 | t_trx.master = get_full_name(); 410 | 411 | t_trx.addr_done = `TRUE; 412 | end 413 | 414 | endtask : collect_addr_read_trx 415 | 416 | 417 | // collect data read @ pos edge 418 | task AXI_master_monitor::collect_data_read_trx(); 419 | AXI_transfer t_trx; 420 | 421 | forever begin 422 | @(posedge m_vif.AXI_ACLK iff (m_vif.AXI_RVALID == 1'b1 && 423 | m_vif.AXI_RREADY == 1'b1)); 424 | 425 | if (m_rd_queue.fd_queued(m_vif.AXI_RID)) begin 426 | t_trx = m_rd_queue.peak_trx(); 427 | 428 | if (t_trx.addr_done==`TRUE) begin 429 | 430 | t_trx.data.push_back( dec_data(m_vif.AXI_RDATA)); 431 | t_trx.resp = dec_resp(m_vif.AXI_RRESP); 432 | 433 | t_trx.used_cycle = t_trx.used_cycle + 1; 434 | 435 | if (m_vif.AXI_RLAST == 1'b1) begin 436 | t_trx.data_done = `TRUE; 437 | t_trx.end_cycle = m_cycle; 438 | t_trx.end_time = $time; 439 | void'(end_tr(t_trx)); 440 | 441 | // Send transfer to scoreboard via TLM write() 442 | item_collected_port.write(t_trx); 443 | dump_trx(t_trx); 444 | m_rd_queue.del_queued(m_vif.AXI_RID); 445 | end 446 | end 447 | end 448 | end 449 | 450 | endtask : collect_data_read_trx 451 | 452 | 453 | /*************************************************************************** 454 | IVB-NOTE : OPTIONAL : master Monitor Protocol Checks : Checks 455 | ------------------------------------------------------------------------- 456 | Add protocol checks within the perform_checks() method. 457 | ***************************************************************************/ 458 | 459 | // perform read queue check 460 | task AXI_master_monitor::perform_write_checks(); 461 | AXI_transfer t_trx; 462 | AXI_map t_map; 463 | 464 | forever begin 465 | @(negedge m_vif.AXI_ACLK); 466 | 467 | foreach (m_wr_queue.m_queue[i]) begin 468 | $cast(t_map,m_wr_queue.m_queue[i]); 469 | t_trx = t_map.m_trx; 470 | 471 | if (m_cycle - t_trx.begin_cycle >= m_conf.tolerate_wt_delay) 472 | `uvm_error("OUTTIMING", {$psprintf("trx @addr %h @time %d+%d is out of %d write trx time", 473 | t_trx.addr, 474 | t_trx.begin_cycle, 475 | m_conf.tolerate_wt_delay, 476 | m_cycle), get_full_name()}) 477 | 478 | end 479 | end 480 | 481 | endtask : perform_write_checks 482 | 483 | // perform write queue check 484 | task AXI_master_monitor::perform_read_checks(); 485 | AXI_transfer t_trx; 486 | AXI_map t_map; 487 | 488 | forever begin 489 | @(negedge m_vif.AXI_ACLK); 490 | 491 | foreach (m_rd_queue.m_queue[i]) begin 492 | $cast(t_map,m_rd_queue.m_queue[i]); 493 | t_trx = t_map.m_trx; 494 | 495 | if (m_cycle - t_trx.begin_cycle >= m_conf.tolerate_wt_delay) 496 | `uvm_error("OUTTIMING", {$psprintf("trx @addr %h @time %d+%d is out of %d read trx time", 497 | t_trx.addr, 498 | t_trx.begin_cycle, 499 | m_conf.tolerate_rd_delay, 500 | m_cycle), get_full_name()}) 501 | 502 | end 503 | end 504 | 505 | endtask : perform_read_checks 506 | 507 | 508 | /*************************************************************************** 509 | IVB-NOTE : OPTIONAL : master Monitor Coverage : Coverage 510 | ------------------------------------------------------------------------- 511 | Modify the master_transfer_cg coverage group to match your protocol. 512 | Add new coverage groups, and edit the perform_coverage() method to sample 513 | them. 514 | ***************************************************************************/ 515 | 516 | // Triggers coverage events 517 | task AXI_master_monitor::perform_write_coverage(); 518 | // master_transfer_cg.sample(); 519 | endtask : perform_write_coverage 520 | 521 | 522 | task AXI_master_monitor::perform_read_coverage(); 523 | // master_transfer_cg.sample(); 524 | endtask : perform_read_coverage 525 | 526 | 527 | // UVM report() phase 528 | function void AXI_master_monitor::report(); 529 | `uvm_info(get_type_name(), $psprintf("\nReport: AXI master monitor collected %0d transfers", m_num_col), 530 | UVM_LOW) 531 | endfunction : report 532 | 533 | `endif // AXI_MASTER_MONITOR_SV 534 | 535 | -------------------------------------------------------------------------------- /sv/axi_master_recorder.sv: -------------------------------------------------------------------------------- 1 | // record TRX to database 2 | -------------------------------------------------------------------------------- /sv/axi_master_sequencer.sv: -------------------------------------------------------------------------------- 1 | /*-------------------------------------- 2 | // AXI master sequencer 3 | // file : axi_master_sequencer.sv 4 | // author : SeanChen 5 | // date : 2013/05/06 6 | // description : it's a driver bridge 7 | ---------------------------------------*/ 8 | 9 | `ifndef AXI_MASTER_SEQUENCER_SV 10 | `define AXI_MASTER_SEQUENCER_SV 11 | 12 | class AXI_master_sequencer extends uvm_sequencer #(AXI_transfer); 13 | 14 | `uvm_component_utils_begin(AXI_master_sequencer) 15 | `uvm_component_utils_end 16 | 17 | // Constructor - required syntax for UVM automation and utilities 18 | function new (string name, uvm_component parent); 19 | super.new(name, parent); 20 | endfunction : new 21 | 22 | virtual function void build_phase(uvm_phase phase); 23 | super.build_phase(phase); 24 | endfunction : build_phase 25 | 26 | endclass : AXI_master_sequencer 27 | 28 | `endif // AXI_master_sequencer 29 | -------------------------------------------------------------------------------- /sv/axi_pat.sv: -------------------------------------------------------------------------------- 1 | # pat .... like STL file format, user can define the virtual transaction model to replay the bus transaction function 2 | -------------------------------------------------------------------------------- /sv/axi_pkg.sv: -------------------------------------------------------------------------------- 1 | /*-------------------------------------- 2 | // AXI PKG 3 | // file : axi_pkg.sv 4 | // author : SeanChen 5 | // date : 2013/05/06 6 | // notes 7 | ---------------------------------------*/ 8 | 9 | `ifndef AXI_PKG_SV 10 | `define AXI_PKG_SV 11 | 12 | package axi_pkg; 13 | 14 | // Import the UVM class library and UVM automation macros 15 | import uvm_pkg::*; 16 | `include "uvm_macros.svh" 17 | 18 | `include "axi_type.sv" 19 | `include "axi_conf.sv" 20 | `include "axi_transfer.sv" 21 | `include "axi_common.sv" 22 | 23 | `include "axi_master_monitor.sv" 24 | `include "axi_master_driver.sv" 25 | `include "axi_master_sequencer.sv" 26 | //`include "axi_master_recorder.sv" 27 | `include "axi_master_agent.sv" 28 | 29 | `include "axi_slave_monitor.sv" 30 | `include "axi_slave_driver.sv" 31 | `include "axi_slave_sequencer.sv" 32 | `include "axi_slave_agent.sv" 33 | 34 | `include "axi_env.sv" 35 | 36 | endpackage : axi_pkg 37 | `endif // AXI_PKG_SV 38 | -------------------------------------------------------------------------------- /sv/axi_slave_agent.sv: -------------------------------------------------------------------------------- 1 | 2 | /*-------------------------------------- 3 | // AXI slave agent 4 | // file : axi4_slave_agent.sv 5 | // author : SeanChen 6 | // date : 2013/05/06 7 | // description : slave agent, contain monitor or sequencer if the ACTIVE is on 8 | // date : 2013/05/06 9 | ---------------------------------------*/ 10 | 11 | `ifndef AXI_SLAVE_AGENT_SV 12 | `define AXI_SLAVE_AGENT_SV 13 | 14 | class AXI_slave_agent extends uvm_agent; 15 | 16 | AXI_slave_conf m_conf; 17 | virtual interface AXI_vif m_vif; 18 | 19 | AXI_slave_driver m_driver; 20 | AXI_slave_sequencer m_sequencer; 21 | AXI_slave_monitor m_monitor; 22 | 23 | // reserve fields 24 | `uvm_component_utils_begin(AXI_slave_agent) 25 | `uvm_component_utils_end 26 | 27 | 28 | // constructor 29 | function new(string name, uvm_component parent); 30 | super.new(name, parent); 31 | endfunction : new 32 | 33 | 34 | // build phase 35 | virtual function void build_phase(uvm_phase phase); 36 | super.build(); 37 | 38 | m_monitor = AXI_slave_monitor::type_id::create("m_monitor", this); 39 | if (m_conf.is_active == UVM_ACTIVE) begin 40 | m_driver = AXI_slave_driver::type_id::create("m_driver", this); 41 | m_sequencer = AXI_slave_sequencer::type_id::create("m_sequencer", this); 42 | end 43 | 44 | m_monitor.assign_conf(m_conf); 45 | if (m_conf.is_active == UVM_ACTIVE) begin 46 | // m_sequencer.assign_conf(m_conf); 47 | m_driver.assign_conf(m_conf); 48 | end 49 | 50 | m_monitor.assign_vi(m_vif); 51 | if (m_conf.is_active == UVM_ACTIVE) begin 52 | // m_sequencer.assign_vi(axi_vif); 53 | m_driver.assign_vi(m_vif); 54 | end 55 | 56 | endfunction : build_phase 57 | 58 | 59 | // connect phase 60 | virtual function void connect_phase(uvm_phase phase); 61 | if (m_conf.is_active == UVM_ACTIVE) begin 62 | m_driver.seq_item_port.connect(m_sequencer.seq_item_export); 63 | 64 | m_monitor.item_write_port.connect(m_driver.item_write_imp); 65 | m_monitor.item_read_port.connect(m_driver.item_read_imp); 66 | end 67 | endfunction : connect_phase 68 | 69 | 70 | // assign virtual interface 71 | virtual function void assign_vi(virtual interface AXI_vif axi_vif); 72 | m_vif = axi_vif; 73 | endfunction : assign_vi 74 | 75 | 76 | // assign configure 77 | virtual function void assign_conf(AXI_slave_conf axi_conf); 78 | m_conf = axi_conf; 79 | endfunction : assign_conf 80 | 81 | endclass : AXI_slave_agent 82 | 83 | `endif // AXI_SLAVE_AGENT_SV 84 | 85 | -------------------------------------------------------------------------------- /sv/axi_slave_driver.sv: -------------------------------------------------------------------------------- 1 | /*-------------------------------------- 2 | // AXI slave driver 3 | // file : axi_slave_driver.sv 4 | // author : SeanChen 5 | // date : 2013/05/06 6 | // brief : slave driver, transfer TLM level info to pin level info 7 | ---------------------------------------*/ 8 | 9 | `ifndef AXI_slave_DRIVER_SV 10 | `define AXI_slave_DRIVER_SV 11 | 12 | class AXI_slave_driver extends uvm_driver #(AXI_transfer); 13 | 14 | virtual interface AXI_vif m_vif; 15 | AXI_slave_conf m_conf; 16 | 17 | int unsigned m_num_sent; 18 | 19 | AXI_transfer m_wr_queue[$]; 20 | AXI_transfer m_rd_queue[$]; 21 | 22 | int unsigned m_mem[int unsigned]; 23 | 24 | uvm_analysis_imp#(AXI_transfer, AXI_slave_driver) item_write_imp; 25 | uvm_analysis_imp#(AXI_transfer, AXI_slave_driver) item_read_imp; 26 | 27 | // reserve fields 28 | `uvm_component_utils_begin(AXI_slave_driver) 29 | `uvm_field_int (m_num_sent, UVM_ALL_ON) 30 | `uvm_component_utils_end 31 | 32 | // Constructor - required syntax for UVM automation and utilities 33 | function new (string name, uvm_component parent); 34 | super.new(name, parent); 35 | 36 | // TLM 37 | item_write_imp = new("item_write_imp", this); 38 | item_read_imp = new("item_read_imp", this); 39 | 40 | endfunction : new 41 | 42 | // Additional class methods 43 | extern virtual function void assign_vi(virtual interface AXI_vif vif); 44 | extern virtual function void assign_conf(AXI_slave_conf conf); 45 | 46 | extern virtual task run_phase(uvm_phase phase); 47 | extern virtual function void connect_phase(uvm_phase phase); 48 | extern virtual protected task get_and_drive(); 49 | extern virtual protected task reset_signals(); 50 | extern virtual protected task drive_transfer(); 51 | // extern virtual function void report(); 52 | 53 | extern virtual protected task sent_addr_write_trx(); 54 | extern virtual protected task sent_data_write_trx(); 55 | extern virtual protected task sent_resp_write_trx(); 56 | 57 | extern virtual protected task sent_addr_read_trx(); 58 | extern virtual protected task sent_data_read_trx(); 59 | 60 | extern virtual protected task wait_for_reset(); 61 | extern virtual protected task sent_trx_to_seq(); 62 | 63 | // TLM analsis port call back 64 | extern virtual function void write(AXI_transfer trx); 65 | 66 | // extern virtual protected task 67 | endclass : AXI_slave_driver 68 | 69 | 70 | function void AXI_slave_driver::assign_vi(virtual interface AXI_vif vif); 71 | m_vif = vif; 72 | endfunction 73 | 74 | 75 | function void AXI_slave_driver::assign_conf(AXI_slave_conf conf); 76 | m_conf = conf; 77 | endfunction 78 | 79 | 80 | //UVM connect_phase 81 | function void AXI_slave_driver::connect_phase(uvm_phase phase); 82 | super.connect_phase(phase); 83 | 84 | if (!uvm_config_db#(virtual interface AXI_vif)::get(this, "", "m_vif", m_vif)) 85 | `uvm_error("NOVIF",{"virtual interface must be set for: ",get_full_name(),".m_vif"}) 86 | 87 | assert(m_conf!=null); 88 | // if (!uvm_config_db#(AXI_slave_conf)::get(this, "", "m_conf", m_conf)) 89 | // `uvm_error("NOCONF",{"axi conf must be set for: ", get_full_name(), ".m_conf"}) 90 | 91 | endfunction : connect_phase 92 | 93 | 94 | // UVM run() phase 95 | task AXI_slave_driver::run_phase(uvm_phase phase); 96 | fork 97 | get_and_drive(); 98 | reset_signals(); 99 | join 100 | endtask : run_phase 101 | 102 | 103 | // Gets transfers from the sequencer and passes them to the driver. 104 | task AXI_slave_driver::get_and_drive(); 105 | wait_for_reset(); 106 | sent_trx_to_seq(); 107 | endtask : get_and_drive 108 | 109 | 110 | // Reset all slave signals 111 | task AXI_slave_driver::reset_signals(); 112 | forever begin 113 | @(posedge m_vif.AXI_ARESET_N); 114 | `uvm_info(get_type_name(), "Reset observed", UVM_MEDIUM) 115 | // addr write 116 | m_vif.AXI_AWREADY <= 0; 117 | // data write 118 | m_vif.AXI_WREADY <= 0; 119 | // resp write 120 | m_vif.AXI_BID <= 0; 121 | // .... 122 | end 123 | endtask : reset_signals 124 | 125 | 126 | task AXI_slave_driver::wait_for_reset(); 127 | wait(!m_vif.AXI_ARESET_N) 128 | `uvm_info(get_type_name(), "Reset dropped", UVM_MEDIUM) 129 | 130 | endtask : wait_for_reset 131 | 132 | 133 | // get next trx when reset has already done 134 | // default : set init memory map 135 | task AXI_slave_driver::sent_trx_to_seq(); 136 | drive_transfer(); 137 | endtask : sent_trx_to_seq 138 | 139 | 140 | // Gets a transfer and drive it into the DUT 141 | // ps addr_write and data_write can be the same time 142 | task AXI_slave_driver::drive_transfer(); 143 | fork 144 | sent_addr_read_trx(); 145 | sent_data_read_trx(); 146 | 147 | sent_addr_write_trx(); 148 | sent_data_write_trx(); 149 | sent_resp_write_trx(); 150 | join 151 | endtask : drive_transfer 152 | 153 | 154 | // sent addr write trx 155 | task AXI_slave_driver::sent_addr_write_trx(); 156 | 157 | forever begin 158 | `delay(m_conf.half_cycle); 159 | m_vif.AXI_AWREADY <= 1'b0; 160 | repeat($urandom_range(4,8)) @(posedge m_vif.AXI_ACLK); 161 | 162 | `delay(m_conf.half_cycle); 163 | m_vif.AXI_AWREADY <= 1'b1; 164 | @(posedge m_vif.AXI_ACLK); 165 | 166 | // hold until AWVALID received 167 | while(!m_vif.AXI_AWVALID) @(posedge m_vif.AXI_ACLK); 168 | end 169 | 170 | endtask : sent_addr_write_trx 171 | 172 | 173 | // sent data write trx 174 | task AXI_slave_driver::sent_data_write_trx(); 175 | 176 | forever begin 177 | `delay(m_conf.half_cycle); 178 | m_vif.AXI_WREADY <= 1'b0; 179 | repeat($urandom_range(4,8)) @(posedge m_vif.AXI_ACLK); 180 | 181 | `delay(m_conf.half_cycle); 182 | m_vif.AXI_WREADY <= 1'b1; 183 | @(posedge m_vif.AXI_ACLK); 184 | 185 | // hold until WVALID received 186 | while(!m_vif.AXI_WVALID) @(posedge m_vif.AXI_ACLK); 187 | 188 | // continuous hold cycle for burst case 189 | `delay(m_conf.half_cycle); 190 | m_vif.AXI_WREADY <= 1'b1; 191 | repeat($urandom_range(4,16)) @(posedge m_vif.AXI_ACLK); 192 | end 193 | 194 | endtask : sent_data_write_trx 195 | 196 | 197 | // sent data resp trx collected resp to trx 198 | // return priority is based by Qos 199 | task AXI_slave_driver::sent_resp_write_trx(); 200 | AXI_transfer t_trx; 201 | 202 | forever begin 203 | 204 | repeat(m_wr_queue.size()==0) @(posedge m_vif.AXI_ACLK); 205 | 206 | if (m_wr_queue.size()!=0) begin 207 | t_trx = m_wr_queue[0]; 208 | 209 | repeat(t_trx.resp_wt_delay)@(posedge m_vif.AXI_ACLK); 210 | 211 | // sent trx 212 | `delay(m_conf.half_cycle); 213 | m_vif.AXI_BVALID <= 1'b1; 214 | m_vif.AXI_BID <= t_trx.id; 215 | m_vif.AXI_BRESP <= OKAY; 216 | @(posedge m_vif.AXI_ACLK); 217 | 218 | // hold until BREADY received 219 | while(!m_vif.AXI_BREADY) @(posedge m_vif.AXI_ACLK); 220 | void'(m_wr_queue.pop_front()); 221 | 222 | // free trx 223 | `delay(m_conf.half_cycle); 224 | m_vif.AXI_BVALID <= 1'b0; 225 | @(posedge m_vif.AXI_ACLK); 226 | 227 | end 228 | end 229 | 230 | endtask : sent_resp_write_trx 231 | 232 | 233 | // sent addr read trx 234 | task AXI_slave_driver::sent_addr_read_trx(); 235 | 236 | forever begin 237 | `delay(m_conf.half_cycle); 238 | m_vif.AXI_ARREADY <= 1'b0; 239 | repeat($urandom_range(4,8)) @(posedge m_vif.AXI_ACLK); 240 | 241 | `delay(m_conf.half_cycle); 242 | m_vif.AXI_ARREADY <= 1'b1; 243 | @(posedge m_vif.AXI_ACLK); 244 | 245 | // hold until ARVALID received 246 | while(!m_vif.AXI_ARVALID) @(posedge m_vif.AXI_ACLK); 247 | end 248 | 249 | endtask : sent_addr_read_trx 250 | 251 | 252 | // sent data read trx 253 | task AXI_slave_driver::sent_data_read_trx(); 254 | AXI_transfer t_trx; 255 | int unsigned i = 0; 256 | 257 | forever begin 258 | 259 | repeat(m_rd_queue.size()==0) @(posedge m_vif.AXI_ACLK); 260 | 261 | if (m_rd_queue.size()!=0) begin 262 | t_trx = m_rd_queue[0]; 263 | i = 0; 264 | 265 | repeat(m_conf.data_rd_delay) @(posedge m_vif.AXI_ACLK); 266 | 267 | // sent trx 268 | while (i!=t_trx.len+1) begin 269 | 270 | `delay(m_conf.half_cycle); 271 | m_vif.AXI_RVALID <= 1'b1; 272 | m_vif.AXI_RDATA <= m_mem[t_trx.mem_addrs[i]]; 273 | m_vif.AXI_RID <= t_trx.id; 274 | m_vif.AXI_RRESP <= OKAY; 275 | m_vif.AXI_RLAST <= (i==t_trx.len)? 1'b1 : 1'b0; 276 | @(posedge m_vif.AXI_ACLK); 277 | 278 | if (m_vif.AXI_RREADY && m_vif.AXI_RVALID) 279 | i = i+1; 280 | end 281 | 282 | // hold until finsih 283 | 284 | // free trx 285 | void'(m_rd_queue.pop_front()); 286 | 287 | `delay(m_conf.half_cycle); 288 | m_vif.AXI_RVALID <= 1'b0; 289 | m_vif.AXI_RLAST <= 1'b0; 290 | @(posedge m_vif.AXI_ACLK); 291 | 292 | end 293 | end 294 | 295 | endtask : sent_data_read_trx 296 | 297 | 298 | // TLM analysis port 299 | function void AXI_slave_driver::write(AXI_transfer trx); 300 | 301 | if (trx.rw == WRITE && trx.itype == SLAVE) begin 302 | m_wr_queue.push_back(trx); 303 | 304 | foreach (trx.mem_addrs[i]) begin 305 | m_mem[trx.mem_addrs[i]] = trx.data[i]; 306 | end 307 | 308 | end else if (trx.rw == READ && trx.itype == SLAVE) begin 309 | m_rd_queue.push_back(trx); 310 | end 311 | 312 | endfunction : write 313 | 314 | `endif // AXI_slave_DRIVER_SV 315 | 316 | 317 | -------------------------------------------------------------------------------- /sv/axi_slave_monitor.sv: -------------------------------------------------------------------------------- 1 | 2 | `ifndef AXI_SLAVE_MONITOR_SV 3 | `define AXI_SLAVE_MONITOR_SV 4 | 5 | class AXI_slave_monitor extends uvm_monitor; 6 | 7 | virtual interface AXI_vif m_vif; 8 | AXI_slave_conf m_conf; 9 | 10 | // cycle count 11 | longint m_cycle; 12 | 13 | // Count transfers collected 14 | int m_num_col; 15 | 16 | // trx stored queue 17 | AXI_queue m_wr_queue; 18 | AXI_queue m_rd_queue; 19 | 20 | // trx map table 21 | AXI_mem_map m_mem_map; 22 | 23 | // recorder 24 | integer m_file; 25 | 26 | // The following two bits are used to control whether checks and coverage are 27 | // done in the monitor 28 | bit checks_enable = 1; 29 | bit coverage_enable = 1; 30 | 31 | // The current AXI_transfer 32 | protected AXI_transfer trans_collected; 33 | 34 | // This TLM port is used to connect the monitor to the scoreboard 35 | uvm_analysis_port #(AXI_transfer) item_collected_port; 36 | 37 | // This TLM port is used to connect the monitor to driver, that can help driver to identify 38 | // the trx mapping table based on trx's ID... 39 | uvm_analysis_port #(AXI_transfer) item_write_port; 40 | uvm_analysis_port #(AXI_transfer) item_read_port; 41 | 42 | /*-------------------------------- 43 | // Covergroup for transfer 44 | ----------------------------------*/ 45 | covergroup slave_transfer_cg; 46 | TRANS_ADDR : coverpoint trans_collected.addr { 47 | bins ZERO = {0}; 48 | bins NON_ZERO = {[1:8'hff]}; 49 | } 50 | TRANS_DIRECTION : coverpoint trans_collected.rw { 51 | bins READ = {READ}; 52 | bins WRITE = {WRITE}; 53 | } 54 | TRANS_LEN : coverpoint trans_collected.len { 55 | 56 | } 57 | TRANS_SIZE : coverpoint trans_collected.size { 58 | } 59 | 60 | // TRANS_DATA : coverpoint trans_collected.data { 61 | // bins ZERO = {0}; 62 | // bins NON_ZERO = {[1:8'hff]}; 63 | // } 64 | TRANS_ADDR_X_TRANS_DIRECTION: cross TRANS_ADDR, TRANS_DIRECTION; 65 | endgroup : slave_transfer_cg 66 | 67 | // Provide UVM automation and utility methods 68 | `uvm_component_utils_begin(AXI_slave_monitor) 69 | `uvm_field_int (m_num_col, UVM_DEFAULT) 70 | `uvm_field_int (checks_enable, UVM_ALL_ON) 71 | `uvm_field_int (coverage_enable, UVM_ALL_ON) 72 | `uvm_component_utils_end 73 | 74 | 75 | // Constructor - required syntax for UVM automation and utilities 76 | function new (string name, uvm_component parent); 77 | super.new(name, parent); 78 | 79 | // Create the covergroup 80 | slave_transfer_cg = new(); 81 | slave_transfer_cg.set_inst_name("slave_transfer_cg"); 82 | 83 | // Create the TLM port 84 | item_collected_port = new("item_collected_port", this); 85 | 86 | item_write_port = new("item_write_port", this); 87 | item_read_port = new("item_read_port", this); 88 | 89 | // wr/rd queue 90 | m_wr_queue = new(); 91 | m_rd_queue = new(); 92 | 93 | // mem map 94 | m_mem_map = new(); 95 | 96 | // as the same as uvm_recorder 97 | m_file = $fopen({$psprintf("%h.trx", this)}, "w"); 98 | endfunction : new 99 | 100 | // Additional class methods 101 | extern virtual function void assign_vi(virtual interface AXI_vif vif); 102 | extern virtual function void assign_conf(AXI_slave_conf conf); 103 | 104 | extern virtual task run_phase(uvm_phase phase); 105 | extern virtual function void connect_phase(uvm_phase phase); 106 | extern virtual protected task collect_write_transfer(); 107 | extern virtual protected task collect_read_transfer(); 108 | extern virtual protected task perform_write_checks(); 109 | extern virtual protected task perform_read_checks(); 110 | extern virtual protected task perform_write_coverage(); 111 | extern virtual protected task perform_read_coverage(); 112 | extern virtual function void report(); 113 | extern virtual protected function void dump_trx(AXI_transfer trx); 114 | extern virtual protected function void head_trx(); 115 | 116 | extern virtual protected task collect_addr_write_trx(); 117 | extern virtual protected task collect_data_write_trx(); 118 | extern virtual protected task collect_resp_write_trx(); 119 | extern virtual protected task collect_addr_read_trx(); 120 | extern virtual protected task collect_data_read_trx(); 121 | 122 | extern virtual protected task collect_cycle_count(); 123 | 124 | endclass : AXI_slave_monitor 125 | 126 | 127 | function void AXI_slave_monitor::assign_vi(virtual interface AXI_vif vif); 128 | m_vif = vif; 129 | endfunction : assign_vi 130 | 131 | 132 | function void AXI_slave_monitor::assign_conf(AXI_slave_conf conf); 133 | m_conf = conf; 134 | endfunction : assign_conf 135 | 136 | 137 | //UVM connect_phase 138 | function void AXI_slave_monitor::connect_phase(uvm_phase phase); 139 | super.connect_phase(phase); 140 | 141 | if (!uvm_config_db#(virtual AXI_vif)::get(this, "", "m_vif", m_vif)) 142 | `uvm_error("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"}) 143 | 144 | assert(m_conf!=null); 145 | // if (!uvm_config_db#(AXI_slave_conf)::get(this, "", "m_conf", m_conf)) 146 | // `uvm_error("NOCONF",{"axi conf must be set for: ", get_full_name(), ".m_conf"}) 147 | 148 | head_trx(); 149 | endfunction : connect_phase 150 | 151 | 152 | function void AXI_slave_monitor::head_trx(); 153 | 154 | $fwrite(m_file, { 155 | $psprintf("# %s,\n", get_full_name())}); 156 | 157 | // buildup header for trx recorder 158 | $fwrite(m_file, { 159 | $psprintf("# %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n", 160 | "begin_cycle", 161 | "end_cycle", 162 | "used_cycle", 163 | "begin_time", 164 | "end_time", 165 | "r/w", 166 | "addr", 167 | "len", 168 | "size", 169 | "burst", 170 | "datas",) 171 | }); 172 | 173 | endfunction : head_trx 174 | 175 | 176 | // UVM run() phase 177 | task AXI_slave_monitor::run_phase(uvm_phase phase); 178 | `uvm_info(get_type_name(), "sssss", UVM_LOW) 179 | fork 180 | collect_cycle_count(); 181 | collect_write_transfer(); 182 | collect_read_transfer(); 183 | join 184 | endtask : run_phase 185 | 186 | 187 | /*************************************************************************** 188 | IVB-NOTE : REQUIRED : slave Monitor : Monitors 189 | ------------------------------------------------------------------------- 190 | Modify the collect_transfers() method to match your protocol. 191 | Note that if you change/add signals to the physical interface, you must 192 | also change this method. 193 | ***************************************************************************/ 194 | 195 | task AXI_slave_monitor::collect_cycle_count(); 196 | forever begin 197 | @(posedge m_vif.AXI_ACLK); 198 | m_cycle += 1; 199 | end 200 | endtask : collect_cycle_count 201 | 202 | 203 | task AXI_slave_monitor::collect_write_transfer(); 204 | fork 205 | collect_addr_write_trx(); 206 | collect_data_write_trx(); 207 | collect_resp_write_trx(); 208 | 209 | if (checks_enable) 210 | perform_write_checks(); 211 | if (coverage_enable) 212 | perform_write_coverage(); 213 | join 214 | endtask : collect_write_transfer 215 | 216 | 217 | task AXI_slave_monitor::collect_read_transfer(); 218 | fork 219 | collect_addr_read_trx(); 220 | collect_data_read_trx(); 221 | 222 | if (checks_enable) 223 | perform_read_checks(); 224 | if (coverage_enable) 225 | perform_read_coverage(); 226 | join 227 | endtask : collect_read_transfer 228 | 229 | 230 | // collect addr write @ pos edge 231 | task AXI_slave_monitor::collect_addr_write_trx(); 232 | AXI_transfer t_trx; 233 | 234 | // check write pool, if the W(id) is not found,then create a new one 235 | // and recored it's timing 236 | 237 | forever begin 238 | @(posedge m_vif.AXI_ACLK iff (m_vif.AXI_AWVALID == 1'b1 && 239 | m_vif.AXI_AWREADY == 1'b1)); 240 | 241 | if (!m_wr_queue.fd_queued(m_vif.AXI_AWID)) begin 242 | m_wr_queue.en_queued(m_vif.AXI_AWID); 243 | t_trx = m_wr_queue.peak_trx(); 244 | t_trx.begin_cycle = m_cycle; 245 | t_trx.begin_time = $time; 246 | void'(begin_tr(t_trx, {$psprintf("axi_slave_monitor[%h]", this)})); 247 | end 248 | 249 | if (m_conf.start_addr > m_vif.AXI_AWADDR || m_conf.end_addr < m_vif.AXI_AWADDR) 250 | `uvm_error("NOADDRMATCH", {$psprintf("AWADDR %h is not found in [%h:%h]", 251 | m_vif.AXI_AWADDR, 252 | m_conf.start_addr, 253 | m_conf.end_addr), get_full_name()}) 254 | 255 | t_trx = m_wr_queue.peak_trx(); 256 | t_trx.rw = WRITE; 257 | t_trx.addr = dec_addr (m_vif.AXI_AWADDR); 258 | t_trx.id = dec_id (m_vif.AXI_AWID); 259 | t_trx.region = dec_region(m_vif.AXI_AWREG); 260 | t_trx.len = dec_len (m_vif.AXI_AWLEN); 261 | t_trx.size = dec_byte (m_vif.AXI_AWSIZE); 262 | t_trx.burst = dec_burst (m_vif.AXI_AWBURST); 263 | t_trx.lock = dec_lock (m_vif.AXI_AWLOCK); 264 | t_trx.cache = dec_cache (m_vif.AXI_AWCACHE); 265 | t_trx.prot = dec_prot (m_vif.AXI_AWPROT); 266 | t_trx.qos = dec_qos (m_vif.AXI_AWQOS); 267 | 268 | `uvm_info(get_type_name(), $psprintf("collected write addr addr : %h, len : %h", t_trx.addr, t_trx.len), UVM_LOW) 269 | 270 | // extend trx 271 | m_mem_map.run(t_trx); 272 | t_trx.mem_addrs = m_mem_map.addrs; 273 | t_trx.mem_datas = m_mem_map.datas; 274 | m_mem_map.clear(); 275 | 276 | t_trx.itype = SLAVE; 277 | t_trx.slave = get_full_name(); 278 | 279 | t_trx.addr_done = `TRUE; 280 | 281 | if (t_trx.addr_done == `TRUE && 282 | t_trx.data_done == `TRUE) 283 | item_write_port.write(t_trx); // call back to slave driver 284 | end 285 | 286 | endtask : collect_addr_write_trx 287 | 288 | 289 | // collect data write @ pos edge 290 | task AXI_slave_monitor::collect_data_write_trx(); 291 | AXI_transfer t_trx; 292 | 293 | // check write pool, if the W(id) is not found, create a new one 294 | // and recored it's timing 295 | 296 | forever begin 297 | @(posedge m_vif.AXI_ACLK iff (m_vif.AXI_WVALID == 1'b1 && 298 | m_vif.AXI_WREADY == 1'b1)); 299 | 300 | if (!m_wr_queue.fd_queued(m_vif.AXI_WID)) begin 301 | m_wr_queue.en_queued(m_vif.AXI_WID); 302 | t_trx = m_wr_queue.peak_trx(); 303 | t_trx.begin_cycle = m_cycle; 304 | t_trx.begin_time = $time; 305 | void'(begin_tr(t_trx, {$psprintf("axi_slave_monitor[%h]", this)})); 306 | end 307 | 308 | t_trx = m_wr_queue.peak_trx(); 309 | t_trx.data.push_back( dec_data(m_vif.AXI_WDATA) ); 310 | t_trx.strb.push_back( dec_strb(m_vif.AXI_WSTRB) ); 311 | 312 | t_trx.used_cycle = t_trx.used_cycle + 1; 313 | 314 | if (m_vif.AXI_WLAST == 1'b1) begin 315 | t_trx.data_done = `TRUE; 316 | `uvm_info(get_type_name(), $psprintf("collected write data last"), UVM_LOW) 317 | end 318 | 319 | if (t_trx.addr_done == `TRUE && 320 | t_trx.data_done == `TRUE) 321 | item_write_port.write(t_trx); // call back to slave driver 322 | 323 | end 324 | 325 | endtask : collect_data_write_trx 326 | 327 | 328 | // collect resp write @ neg edge 329 | task AXI_slave_monitor::collect_resp_write_trx(); 330 | AXI_transfer t_trx; 331 | 332 | forever begin 333 | @(negedge m_vif.AXI_ACLK iff (m_vif.AXI_BVALID == 1'b1 && 334 | m_vif.AXI_BREADY == 1'b1)); 335 | 336 | if (m_wr_queue.fd_queued(m_vif.AXI_BID)) begin 337 | t_trx = m_wr_queue.peak_trx(); 338 | 339 | if (t_trx.addr_done == `TRUE && 340 | t_trx.data_done == `TRUE) begin 341 | 342 | t_trx.resp = dec_resp (m_vif.AXI_BRESP); 343 | t_trx.end_cycle = m_cycle; 344 | t_trx.end_time = $time; 345 | void'(end_tr(t_trx)); 346 | 347 | `uvm_info(get_type_name(), $psprintf("collected write data resp"), UVM_LOW) 348 | 349 | // Send transfer to scoreboard via TLM write() 350 | item_collected_port.write(t_trx); 351 | dump_trx(t_trx); 352 | m_wr_queue.del_queued(m_vif.AXI_BID); 353 | end 354 | 355 | end else begin 356 | `uvm_error("NOIDMATCH", {"BID is not found in write queue: ",get_full_name()}) 357 | end 358 | end 359 | 360 | endtask : collect_resp_write_trx 361 | 362 | 363 | // collect addr read @ pos edge 364 | task AXI_slave_monitor::collect_addr_read_trx(); 365 | AXI_transfer t_trx; 366 | 367 | forever begin 368 | @(posedge m_vif.AXI_ACLK iff (m_vif.AXI_ARVALID == 1'b1 && 369 | m_vif.AXI_ARREADY == 1'b1)); 370 | 371 | if (!m_rd_queue.fd_queued(m_vif.AXI_ARID)) begin 372 | m_rd_queue.en_queued(m_vif.AXI_ARID); 373 | t_trx = m_rd_queue.peak_trx(); 374 | t_trx.begin_cycle = m_cycle; 375 | t_trx.begin_time = $time; 376 | void'(begin_tr(t_trx, {$psprintf("axi_slave_monitor[%h]", this)})); 377 | end 378 | 379 | if (m_conf.start_addr > m_vif.AXI_ARADDR || m_conf.end_addr < m_vif.AXI_ARADDR) 380 | `uvm_error("NOADDRMATCH", {$psprintf("ARADDR %h is not found in [%h:%h]", 381 | m_vif.AXI_ARADDR, 382 | m_conf.start_addr, 383 | m_conf.end_addr), get_full_name()}) 384 | 385 | t_trx = m_rd_queue.peak_trx(); 386 | t_trx.rw = READ; 387 | t_trx.addr = dec_addr (m_vif.AXI_ARADDR); 388 | t_trx.id = dec_id (m_vif.AXI_ARID); 389 | t_trx.region = dec_region(m_vif.AXI_ARREG); 390 | t_trx.len = dec_len (m_vif.AXI_ARLEN); 391 | t_trx.size = dec_byte (m_vif.AXI_ARSIZE); 392 | t_trx.burst = dec_burst (m_vif.AXI_ARBURST); 393 | t_trx.lock = dec_lock (m_vif.AXI_ARLOCK); 394 | t_trx.cache = dec_cache (m_vif.AXI_ARCACHE); 395 | t_trx.prot = dec_prot (m_vif.AXI_ARPROT); 396 | t_trx.qos = dec_qos (m_vif.AXI_ARQOS); 397 | 398 | `uvm_info(get_type_name(), $psprintf("collected read addr addr : %h, len %h, id %h", t_trx.addr, t_trx.len, t_trx.id), UVM_LOW) 399 | 400 | // extend trx 401 | m_mem_map.run(t_trx); 402 | t_trx.mem_addrs = m_mem_map.addrs; 403 | t_trx.mem_datas = m_mem_map.datas; 404 | m_mem_map.clear(); 405 | 406 | t_trx.itype = SLAVE; 407 | t_trx.slave = get_full_name(); 408 | t_trx.addr_done = `TRUE; 409 | 410 | item_read_port.write(t_trx); // call back to slave driver 411 | end 412 | 413 | endtask : collect_addr_read_trx 414 | 415 | // collect data read @ neg edge 416 | task AXI_slave_monitor::collect_data_read_trx(); 417 | AXI_transfer t_trx; 418 | 419 | forever begin 420 | @(negedge m_vif.AXI_ACLK iff (m_vif.AXI_RVALID == 1'b1 && 421 | m_vif.AXI_RREADY == 1'b1)); 422 | 423 | if (m_rd_queue.fd_queued(m_vif.AXI_RID)) begin 424 | t_trx = m_rd_queue.peak_trx(); 425 | 426 | if (t_trx.addr_done==`TRUE) begin 427 | 428 | t_trx.data.push_back( dec_data(m_vif.AXI_RDATA) ); 429 | t_trx.resp = dec_resp(m_vif.AXI_RRESP); 430 | 431 | t_trx.used_cycle = t_trx.used_cycle + 1; 432 | 433 | if (m_vif.AXI_RLAST == 1'b1) begin 434 | t_trx.data_done = `TRUE; 435 | t_trx.end_cycle = m_cycle; 436 | t_trx.end_time = $time; 437 | void'(end_tr(t_trx)); 438 | 439 | // Send transfer to scoreboard via TLM write() 440 | item_collected_port.write(t_trx); 441 | dump_trx(t_trx); 442 | m_rd_queue.del_queued(m_vif.AXI_RID); 443 | end 444 | end 445 | end 446 | end 447 | 448 | endtask : collect_data_read_trx 449 | 450 | 451 | // dump trx as the same method as uvm_recorder 452 | function void AXI_slave_monitor::dump_trx(AXI_transfer trx); 453 | 454 | $fwrite(m_file, { 455 | $psprintf("%16h, %16h, %16h, %16h, %16h, %s, %8h, %8h, %8h, %8h, ", 456 | trx.begin_cycle, 457 | trx.end_cycle, 458 | trx.used_cycle, 459 | trx.begin_time, 460 | trx.end_time, 461 | (trx.rw == READ)? "R" : "W", 462 | trx.addr, 463 | trx.len, 464 | trx.size, 465 | trx.burst) 466 | }); 467 | 468 | foreach (trx.data[i]) begin 469 | $fwrite(m_file, { 470 | $psprintf("%8h(%8h), ", 471 | trx.data[i], 472 | trx.strb[i]) 473 | }); 474 | end 475 | 476 | $fwrite(m_file, "\n"); 477 | 478 | endfunction : dump_trx 479 | 480 | /*************************************************************************** 481 | IVB-NOTE : OPTIONAL : slave Monitor Protocol Checks : Checks 482 | ------------------------------------------------------------------------- 483 | Add protocol checks within the perform_checks() method. 484 | ***************************************************************************/ 485 | 486 | // perform read queue check 487 | task AXI_slave_monitor::perform_write_checks(); 488 | AXI_transfer t_trx; 489 | AXI_map t_map; 490 | 491 | forever begin 492 | @(negedge m_vif.AXI_ACLK); 493 | 494 | foreach (m_wr_queue.m_queue[i]) begin 495 | $cast(t_map,m_wr_queue.m_queue[i]); 496 | t_trx = t_map.m_trx; 497 | 498 | if (m_cycle - t_trx.begin_cycle >= m_conf.tolerate_wt_delay) 499 | `uvm_error("OUTTIMING", {$psprintf("trx @addr %h @time %d+%d is out of %d write trx time", 500 | t_trx.addr, 501 | t_trx.begin_cycle, 502 | m_conf.tolerate_wt_delay, 503 | m_cycle), get_full_name()}) 504 | 505 | end 506 | end 507 | 508 | endtask : perform_write_checks 509 | 510 | // perform write queue check 511 | task AXI_slave_monitor::perform_read_checks(); 512 | AXI_transfer t_trx; 513 | AXI_map t_map; 514 | 515 | forever begin 516 | @(negedge m_vif.AXI_ACLK); 517 | 518 | foreach (m_rd_queue.m_queue[i]) begin 519 | $cast(t_map,m_rd_queue.m_queue[i]); 520 | t_trx = t_map.m_trx; 521 | 522 | if (m_cycle - t_trx.begin_cycle >= m_conf.tolerate_wt_delay) 523 | `uvm_error("OUTTIMING", {$psprintf("trx @addr %h @time %d+%d is out of %d read trx time", 524 | t_trx.addr, 525 | t_trx.begin_cycle, 526 | m_conf.tolerate_rd_delay, 527 | m_cycle), get_full_name()}) 528 | 529 | end 530 | end 531 | 532 | endtask : perform_read_checks 533 | 534 | 535 | /*************************************************************************** 536 | IVB-NOTE : OPTIONAL : slave Monitor Coverage : Coverage 537 | ------------------------------------------------------------------------- 538 | Modify the slave_transfer_cg coverage group to match your protocol. 539 | Add new coverage groups, and edit the perform_coverage() method to sample 540 | them. 541 | ***************************************************************************/ 542 | 543 | // Triggers coverage events 544 | task AXI_slave_monitor::perform_write_coverage(); 545 | // slave_transfer_cg.sample(); 546 | endtask : perform_write_coverage 547 | 548 | 549 | task AXI_slave_monitor::perform_read_coverage(); 550 | // slave_transfer_cg.sample(); 551 | endtask : perform_read_coverage 552 | 553 | 554 | // UVM report() phase 555 | function void AXI_slave_monitor::report(); 556 | `uvm_info(get_type_name(), $psprintf("\nReport: AXI slave monitor collected %0d transfers", m_num_col), 557 | UVM_LOW) 558 | endfunction : report 559 | 560 | `endif // AXI_SLAVE_MONITOR_SV 561 | 562 | -------------------------------------------------------------------------------- /sv/axi_slave_recorder.sv: -------------------------------------------------------------------------------- 1 | // record TRX to database 2 | -------------------------------------------------------------------------------- /sv/axi_slave_sequencer.sv: -------------------------------------------------------------------------------- 1 | /*-------------------------------------- 2 | // AXI slave sequencer 3 | // file : axi_slave_sequencer.sv 4 | // author : SeanChen 5 | // date : 2013/05/06 6 | // description : it's a driver bridge 7 | ---------------------------------------*/ 8 | 9 | `ifndef AXI_SLAVE_SEQUENCER_SV 10 | `define AXI_SLAVE_SEQUENCER_SV 11 | 12 | class AXI_slave_sequencer extends uvm_sequencer #(AXI_transfer); 13 | 14 | `uvm_component_utils_begin(AXI_slave_sequencer) 15 | `uvm_component_utils_end 16 | 17 | // Constructor - required syntax for UVM automation and utilities 18 | function new (string name, uvm_component parent); 19 | super.new(name, parent); 20 | endfunction : new 21 | 22 | virtual function void build_phase(uvm_phase phase); 23 | super.build_phase(phase); 24 | endfunction : build_phase 25 | 26 | endclass : AXI_slave_sequencer 27 | 28 | `endif // AXI_slave_sequencer 29 | -------------------------------------------------------------------------------- /sv/axi_transfer.sv: -------------------------------------------------------------------------------- 1 | 2 | /*-------------------------------------- 3 | // AXI transfer 4 | // file : axi_transfer.sv 5 | // author : SeanChen 6 | // date : 2013/05/06 7 | // description: based AXT transfer type (TLM info struct) 8 | ---------------------------------------*/ 9 | 10 | `ifndef AXI_TRANSFER_SV 11 | `define AXI_TRANSFER_SV 12 | 13 | class AXI_base extends uvm_sequence_item; 14 | 15 | rand int unsigned id; 16 | rand int unsigned transmit_delay = 0; 17 | rand int unsigned addr_wt_delay = 0; 18 | rand int unsigned data_wt_delay = 0; 19 | rand int unsigned resp_wt_delay = 0; 20 | rand int unsigned addr_rd_delay = 0; 21 | rand int unsigned data_rd_delay = 0; 22 | 23 | rand int unsigned data_bytes = 4; 24 | rand int unsigned half_cycle = 0; 25 | 26 | rand longint begin_cycle = 0; 27 | rand longint end_cycle = 0; 28 | 29 | rand longint begin_time = 0; 30 | rand longint end_time = 0; 31 | 32 | rand longint used_cycle = 0; 33 | 34 | string master = ""; 35 | string slave = ""; 36 | 37 | int unsigned records[$]; // record router paths from master to slave 38 | 39 | constraint c_transmit_delay { 1 <= transmit_delay <= 10; } 40 | constraint c_addr_wt_delay { 1 <= addr_wt_delay <= 10; } 41 | constraint c_data_wt_delay { 1 <= data_wt_delay <= 10; } 42 | constraint c_resp_wt_delay { 1 <= resp_wt_delay <= 10; } 43 | constraint c_addr_rd_delay { 1 <= addr_rd_delay <= 10; } 44 | constraint c_data_rd_delay { 1 <= data_rd_delay <= 10; } 45 | 46 | `uvm_object_utils_begin(AXI_base) 47 | `uvm_field_int (id, UVM_DEFAULT) 48 | `uvm_field_int (transmit_delay, UVM_DEFAULT) 49 | `uvm_field_int (addr_wt_delay, UVM_DEFAULT) 50 | `uvm_field_int (data_wt_delay, UVM_DEFAULT) 51 | `uvm_field_int (resp_wt_delay, UVM_DEFAULT) 52 | `uvm_field_int (addr_rd_delay, UVM_DEFAULT) 53 | `uvm_field_int (data_rd_delay, UVM_DEFAULT) 54 | `uvm_field_queue_int (records, UVM_DEFAULT) 55 | `uvm_field_int (data_bytes, UVM_DEFAULT) 56 | `uvm_field_int (half_cycle, UVM_DEFAULT) 57 | `uvm_object_utils_end 58 | 59 | function new (string name = "axi_base"); 60 | super.new(name); 61 | endfunction 62 | 63 | endclass : AXI_base 64 | 65 | 66 | class AXI_transfer extends AXI_base; 67 | 68 | rand itype_enum itype; 69 | rand direction_enum rw; 70 | rand int unsigned addr; 71 | rand int unsigned region; 72 | rand len_enum len; 73 | rand byte_enum size; 74 | rand burst_enum burst; 75 | rand lock_enum lock; 76 | rand cache_enc_enum cache; 77 | rand protect_enc_enum prot; 78 | rand int unsigned qos; 79 | rand int unsigned strb[$]; 80 | rand response_enum resp; 81 | rand int unsigned data[$]; 82 | 83 | rand bit addr_done; 84 | rand bit data_done; 85 | 86 | rand int unsigned mem_datas[$]; 87 | rand int unsigned mem_addrs[$]; 88 | 89 | `uvm_object_utils_begin(AXI_transfer) 90 | `uvm_field_enum (itype_enum, itype, UVM_DEFAULT) 91 | `uvm_field_enum (direction_enum, rw, UVM_DEFAULT) 92 | `uvm_field_int (addr, UVM_DEFAULT) 93 | `uvm_field_int (region, UVM_DEFAULT) 94 | `uvm_field_enum (len_enum, len, UVM_DEFAULT) 95 | `uvm_field_enum (byte_enum, size, UVM_DEFAULT) 96 | `uvm_field_enum (burst_enum, burst, UVM_DEFAULT) 97 | `uvm_field_enum (lock_enum, lock, UVM_DEFAULT) 98 | `uvm_field_enum (cache_enc_enum, cache, UVM_DEFAULT) 99 | `uvm_field_enum (protect_enc_enum, prot, UVM_DEFAULT) 100 | `uvm_field_int (qos, UVM_DEFAULT) 101 | `uvm_field_queue_int(strb, UVM_DEFAULT) 102 | `uvm_field_enum (response_enum, resp, UVM_DEFAULT) 103 | `uvm_field_queue_int(data, UVM_DEFAULT) 104 | `uvm_field_int (addr_done, UVM_DEFAULT) 105 | `uvm_field_int (data_done, UVM_DEFAULT) 106 | `uvm_field_queue_int(mem_datas, UVM_DEFAULT) 107 | `uvm_field_queue_int(mem_addrs, UVM_DEFAULT) 108 | `uvm_object_utils_end 109 | 110 | function new (string name = "axi_transfer"); 111 | super.new(name); 112 | endfunction 113 | 114 | endclass : AXI_transfer 115 | 116 | 117 | `endif // AXI_TRANSFER_SV 118 | 119 | 120 | -------------------------------------------------------------------------------- /sv/axi_type.sv: -------------------------------------------------------------------------------- 1 | `ifndef TTUE 2 | `define TRUE 1 3 | `endif 4 | 5 | `ifndef FALSE 6 | `define FALSE 0 7 | `endif 8 | 9 | 10 | `define delay(d) \ 11 | #d; 12 | 13 | typedef enum { TERAWINS_AXI_DDR, VIRTUAL_SLAVE } slave_type_enum; 14 | typedef enum { VIRTUAL_MASTER } master_type_enum; 15 | 16 | typedef enum { MASTER = 0, SLAVE = 1 } itype_enum; 17 | 18 | typedef enum { READ = 0, WRITE = 1 } direction_enum; 19 | 20 | function dec_direction(int unsigned d); 21 | case(d) 22 | 0 : return READ; 23 | 1 : return WRITE; 24 | default : `uvm_error("NODIRECT",{$psprintf("direct %d must be set for: [0:1]", d)}) 25 | endcase 26 | endfunction : dec_direction 27 | 28 | typedef enum { FIXED = 0, INCR = 1, WRAP = 2, RESERVED_BURST = 3 } burst_enum; 29 | 30 | function burst_enum dec_burst(int unsigned b); 31 | case(b) 32 | 0 : return FIXED; 33 | 1 : return INCR; 34 | 2 : return WRAP; 35 | 3 : return RESERVED_BURST; 36 | default : `uvm_error("NOBURST", {$psprintf("burst %d must be set for [0:3]", b)}) 37 | endcase 38 | endfunction : dec_burst 39 | 40 | typedef enum { BYTE_1 = 0, 41 | BYTE_2 = 1, 42 | BYTE_4 = 2, 43 | BYTE_8 = 3, 44 | BYTE_16 = 4, 45 | BYTE_32 = 5, 46 | BYTE_64 = 6, 47 | BYTE_128 = 7 } byte_enum; 48 | 49 | function byte_enum dec_byte(int unsigned b); 50 | case(b) 51 | 0 : return BYTE_1; 52 | 1 : return BYTE_2; 53 | 2 : return BYTE_4; 54 | 3 : return BYTE_8; 55 | 4 : return BYTE_16; 56 | 5 : return BYTE_32; 57 | 6 : return BYTE_64; 58 | 7 : return BYTE_128; 59 | default : `uvm_error("NOBYTE", {$psprintf("byte %d must be set for [0:7]", b)}) 60 | endcase 61 | endfunction : dec_byte 62 | 63 | typedef enum { LEN_1 = 0, 64 | LEN_2 = 1, 65 | LEN_3 = 2, 66 | LEN_4 = 3, 67 | LEN_5 = 4, 68 | LEN_6 = 5, 69 | LEN_7 = 6, 70 | LEN_8 = 7, 71 | LEN_9 = 8, 72 | LEN_10 = 9, 73 | LEN_11 = 10, 74 | LEN_12 = 11, 75 | LEN_13 = 12, 76 | LEN_14 = 13, 77 | LEN_15 = 14, 78 | LEN_16 = 15, 79 | LEN_17 = 16, 80 | LEN_18 = 17, 81 | LEN_19 = 18, 82 | LEN_20 = 19, 83 | LEN_21 = 20, 84 | LEN_22 = 21, 85 | LEN_23 = 22, 86 | LEN_24 = 23, 87 | LEN_25 = 24, 88 | LEN_26 = 25, 89 | LEN_27 = 26, 90 | LEN_28 = 27, 91 | LEN_29 = 28, 92 | LEN_30 = 29, 93 | LEN_31 = 30, 94 | LEN_32 = 31 } len_enum; 95 | 96 | function len_enum dec_len(int unsigned l); 97 | case(l) 98 | 0 : return LEN_1; 99 | 1 : return LEN_2; 100 | 2 : return LEN_3; 101 | 3 : return LEN_4; 102 | 4 : return LEN_5; 103 | 5 : return LEN_6; 104 | 6 : return LEN_7; 105 | 7 : return LEN_8; 106 | 8 : return LEN_9; 107 | 9 : return LEN_10; 108 | 10 : return LEN_11; 109 | 11 : return LEN_12; 110 | 12 : return LEN_13; 111 | 13 : return LEN_14; 112 | 14 : return LEN_15; 113 | 15 : return LEN_16; 114 | 16 : return LEN_17; 115 | 17 : return LEN_18; 116 | 18 : return LEN_19; 117 | 19 : return LEN_20; 118 | 20 : return LEN_21; 119 | 21 : return LEN_22; 120 | 22 : return LEN_23; 121 | 23 : return LEN_24; 122 | 24 : return LEN_25; 123 | 25 : return LEN_26; 124 | 26 : return LEN_27; 125 | 27 : return LEN_28; 126 | 28 : return LEN_29; 127 | 29 : return LEN_30; 128 | 30 : return LEN_31; 129 | 31 : return LEN_32; 130 | default : `uvm_error("NOBYTE", {$psprintf("len %d must be set for [0:31]", l)}) 131 | endcase 132 | endfunction : dec_len 133 | 134 | // cache dec seq is followed [WA, RA, C, B] 135 | // WA : (Write Allocate) = ARCACHE[3] and AWCACHE[3] 136 | // RA : (Read Allocate) = ARCACHE[2] and AWCACHE[2] 137 | // C : (Cacheable) = ARCACHE[1] and AWCACHE[1] 138 | // B : (Bufferable) = ARCACHE[0] and AWCACHE[0] 139 | typedef enum { NONCACHEABLE_AND_NONBUFFERABLE = 0, 140 | BUFFER_ONLY = 1, 141 | CACHEABLE_NOT_ALLOCATE = 2, 142 | CACHEABLE_AND_BUFFERABLE_NOT_ALLOCATE = 3, 143 | RESERVED_4 = 4, 144 | RESERVED_5 = 5, 145 | CACHEABLE_WRITE_THROUGH_ALLOCATE_ON_READ_ONLY = 6, 146 | CACHEABLE_WRITE_BACK_ALLOCATE_ON_READ_ONLY = 7, 147 | RESERVED_8 = 8, 148 | RESERVED_9 = 9, 149 | CACHEABLE_WRITE_THROUGH_ALLOCATE_ON_WRITE_ONLY = 10, 150 | CACHEABLE_WRITE_BACK_ALLOCATE_ON_WRITE_ONLY = 11, 151 | RESERVED_12 = 12, 152 | RESERVED_13 = 13, 153 | CACHEABLE_WRITE_THROUGH_ALLOCATE_ON_READ_WRITE = 14, 154 | CACHEABLE_WRITE_BACK_ALLOCATE_ON_READ_WRITE = 15 } cache_dec_enum; 155 | 156 | // cache enc seq is followed by WA, RA, C, B 157 | typedef enum { WA0_RA0_C0_B0 = 0, 158 | WA0_RA0_C0_B1 = 1, 159 | WA0_RA0_C1_B0 = 2, 160 | WA0_RA0_C1_B1 = 3, 161 | WA0_RA1_C0_B0 = 4, 162 | WA0_RA1_C0_B1 = 5, 163 | WA0_RA1_C1_B0 = 6, 164 | WA0_RA1_C1_B1 = 7, 165 | WA1_RA0_C0_B0 = 8, 166 | WA1_RA0_C0_B1 = 9, 167 | WA1_RA0_C1_B0 = 10, 168 | WA1_RA0_C1_B1 = 11, 169 | WA1_RA1_C0_B0 = 12, 170 | WA1_RA1_C0_B1 = 13, 171 | WA1_RA1_C1_B0 = 14, 172 | WA1_RA1_C1_B1 = 15 } cache_enc_enum; 173 | 174 | function cache_enc_enum dec_cache(int unsigned b); 175 | case(b) 176 | 0 : return WA0_RA0_C0_B0; 177 | 1 : return WA0_RA0_C0_B1; 178 | 2 : return WA0_RA0_C1_B0; 179 | 3 : return WA0_RA0_C1_B1; 180 | 4 : return WA0_RA1_C0_B0; 181 | 5 : return WA0_RA1_C0_B1; 182 | 6 : return WA0_RA1_C1_B0; 183 | 7 : return WA0_RA1_C1_B1; 184 | 8 : return WA1_RA0_C0_B0; 185 | 9 : return WA1_RA0_C0_B1; 186 | 10 : return WA1_RA0_C1_B0; 187 | 11 : return WA1_RA0_C1_B1; 188 | 12 : return WA1_RA1_C0_B0; 189 | 13 : return WA1_RA1_C0_B1; 190 | 14 : return WA1_RA1_C1_B0; 191 | 15 : return WA1_RA1_C1_B1; 192 | default : `uvm_error("NOCACHE", {$psprintf("cache %d must be set for [0:15]", b)}) 193 | endcase 194 | endfunction : dec_cache 195 | 196 | // protection dec seq is flollowed below 197 | // NORMAL_OR_PRIVILEGED = ARPROT[0] and AWPROT[0] 198 | // SECURE_OR_NONSECURE = ARPROT[1] and AWPROT[1] 199 | // INSTRUCTION_OR_DATA = ARPROT[2] and ARPROT[2] 200 | typedef enum { 201 | NORMAL_OR_PRIVILEGED = 0, 202 | SECURE_OR_NONSECURE = 1, 203 | INSTRUCTION_OR_DATA = 2 } protect_dec_enum; 204 | 205 | // protection dec seq is followd below 206 | typedef enum { 207 | PROT000 = 0, 208 | PROT001 = 1, 209 | PROT010 = 2, 210 | PROT011 = 3, 211 | PROT100 = 4, 212 | PROT101 = 5, 213 | PROT110 = 6, 214 | PROT111 = 7 } protect_enc_enum; 215 | 216 | function protect_enc_enum dec_prot(int unsigned p); 217 | case(p) 218 | 0: return PROT000; 219 | 1: return PROT001; 220 | 2: return PROT010; 221 | 3: return PROT011; 222 | 4: return PROT100; 223 | 5: return PROT101; 224 | 6: return PROT110; 225 | 7: return PROT111; 226 | default : `uvm_error("NOPROTECT", {$psprintf("protect %d must be set for [0:7]", p)}) 227 | endcase 228 | endfunction : dec_prot 229 | 230 | typedef enum { 231 | NORMAL_ACCESS = 0, 232 | EXCLUSIVE_ACCESS = 1, 233 | LOCKED_ACCESS = 2, 234 | RESERVED_ACCESS = 3 } lock_enum; 235 | 236 | function lock_enum dec_lock(int unsigned l); 237 | case(l) 238 | 0: return NORMAL_ACCESS; 239 | 1: return EXCLUSIVE_ACCESS; 240 | 2: return LOCKED_ACCESS; 241 | 3: return RESERVED_ACCESS; 242 | default : `uvm_error("NOPROTECT", {$psprintf("lock %d must be set for [0:3]", l)}) 243 | endcase 244 | endfunction : dec_lock 245 | 246 | typedef enum { 247 | OKAY = 0, 248 | EXOKAY = 1, 249 | SLVERR = 2, 250 | DECERR = 3 } response_enum; 251 | 252 | function response_enum dec_resp(int unsigned r); 253 | case(r) 254 | 0: return OKAY; 255 | 1: return EXOKAY; 256 | 2: return SLVERR; 257 | 3: return DECERR; 258 | default : `uvm_error("NORESPONSE", {$psprintf("response %d must be set for [0:3]", r)}) 259 | endcase 260 | endfunction : dec_resp 261 | 262 | function int unsigned dec_qos(int unsigned q); 263 | 264 | if ($isunknown(q)) 265 | `uvm_error("NOQOS", {$psprintf("qos %s must be set for int", q)}) 266 | 267 | return q; 268 | endfunction : dec_qos 269 | 270 | function int unsigned dec_addr(int unsigned a); 271 | 272 | if ($isunknown(a)) 273 | `uvm_error("NOADDR", {$psprintf("addr %s must be set for int", a)}) 274 | 275 | return a; 276 | endfunction : dec_addr 277 | 278 | function int unsigned dec_data(int unsigned d); 279 | 280 | if ($isunknown(d)) 281 | `uvm_error("NODATA", {$psprintf("data %s must be set for int", d)}) 282 | 283 | return d; 284 | endfunction : dec_data 285 | 286 | function int unsigned dec_id(int unsigned i); 287 | 288 | if ($isunknown(i)) 289 | `uvm_error("NOID", {$psprintf("id %s must be set for int", i)}) 290 | 291 | return i; 292 | endfunction : dec_id 293 | 294 | function int unsigned dec_region(int unsigned r); 295 | 296 | if ($isunknown(r)) 297 | `uvm_error("NOREGION", {$psprintf("region %s must be set for int", r)}) 298 | 299 | return r; 300 | endfunction : dec_region 301 | 302 | function int unsigned dec_strb(int unsigned s); 303 | 304 | if ($isunknown(s)) 305 | `uvm_error("NOSTRB", {$psprintf("strb %s must be set for int", s)}) 306 | 307 | return s; 308 | endfunction : dec_strb 309 | -------------------------------------------------------------------------------- /sv/axi_vif.sv: -------------------------------------------------------------------------------- 1 | 2 | /*-------------------------------------- 3 | // AXI virtual interface 4 | // description : axi virtual interface which is a connection pool interface for DUT and Virtual test 5 | // file : axi_vif.sv 6 | // author : SeanChen 7 | // date : 2013/04/10 8 | ---------------------------------------*/ 9 | 10 | `timescale 1ns/10ps 11 | 12 | interface AXI_vif #( 13 | parameter integer C_AXI_ID_WIDTH = 10, // default 4 14 | parameter integer C_AXI_ADDR_WIDTH = 32, 15 | parameter integer C_AXI_REG_WITH = 4, 16 | parameter integer C_AXI_DATA_WIDTH = 32, 17 | parameter integer C_AXI_LEN_WIDTH = 8, // default 4 18 | parameter integer C_AXI_SIZE_WIDTH = 3, 19 | parameter integer C_AXI_BURST_WIDTH = 2, 20 | parameter integer C_AXI_CACHE_WIDTH = 4, 21 | parameter integer C_AXI_PROT_WIDTH = 3, 22 | parameter integer C_AXI_QOS_WIDTH = 4, 23 | parameter integer C_AXI_STRB_WIDTH = 4, 24 | parameter integer C_AXI_RESP_WIDTH = 2, 25 | parameter integer C_AXI_LOCK_WIDTH = 1, 26 | parameter integer C_AXI_VALID_WIDTH = 1, 27 | parameter integer C_AXI_READY_WIDTH = 1, 28 | parameter integer C_AXI_LAST_WIDTH = 1, 29 | parameter string name = "vif" 30 | 31 | )( input AXI_ACLK, input AXI_ARESET_N); 32 | 33 | // control flags 34 | bit has_checks = 1; 35 | bit has_coverage = 1; 36 | 37 | // AXI global signals 38 | // logic [0:0] AXI_ARESET_N; 39 | // logic [0:0] AXI_ACLK; 40 | 41 | // AXI address write phase 42 | logic [C_AXI_ID_WIDTH-1:0] AXI_AWID; //axi4 remove it 43 | logic [C_AXI_ADDR_WIDTH-1:0] AXI_AWADDR; 44 | logic [C_AXI_REG_WITH-1:0] AXI_AWREG; 45 | logic [C_AXI_LEN_WIDTH-1:0] AXI_AWLEN; 46 | logic [C_AXI_SIZE_WIDTH-1:0] AXI_AWSIZE; 47 | logic [C_AXI_BURST_WIDTH-1:0] AXI_AWBURST; 48 | logic [C_AXI_LOCK_WIDTH-1:0] AXI_AWLOCK; 49 | logic [C_AXI_CACHE_WIDTH-1:0] AXI_AWCACHE; 50 | logic [C_AXI_PROT_WIDTH-1:0] AXI_AWPROT; 51 | logic [C_AXI_QOS_WIDTH-1:0] AXI_AWQOS; 52 | logic [C_AXI_VALID_WIDTH-1:0] AXI_AWVALID; 53 | logic [C_AXI_READY_WIDTH-1:0] AXI_AWREADY; 54 | 55 | // AXI data write phase 56 | logic [C_AXI_ID_WIDTH-1:-0] AXI_WID; 57 | logic [C_AXI_DATA_WIDTH-1:0] AXI_WDATA; 58 | logic [C_AXI_STRB_WIDTH-1:0] AXI_WSTRB; 59 | logic [C_AXI_LAST_WIDTH-1:0] AXI_WLAST; 60 | logic [C_AXI_VALID_WIDTH-1:0] AXI_WVALID; 61 | logic [C_AXI_READY_WIDTH-1:0] AXI_WREADY; 62 | 63 | // AXI response write phase 64 | logic [C_AXI_ID_WIDTH-1:0] AXI_BID; 65 | logic [C_AXI_RESP_WIDTH-1:0] AXI_BRESP; 66 | logic [C_AXI_VALID_WIDTH-1:0] AXI_BVALID; 67 | logic [C_AXI_READY_WIDTH-1:0] AXI_BREADY; 68 | 69 | // AXI address read phase 70 | logic [C_AXI_ID_WIDTH-1:0] AXI_ARID; 71 | logic [C_AXI_ADDR_WIDTH-1:0] AXI_ARADDR; 72 | logic [C_AXI_REG_WITH-1:0] AXI_ARREG; 73 | logic [C_AXI_LEN_WIDTH-1:0] AXI_ARLEN; 74 | logic [C_AXI_SIZE_WIDTH-1:0] AXI_ARSIZE; 75 | logic [C_AXI_BURST_WIDTH-1:0] AXI_ARBURST; 76 | logic [C_AXI_LOCK_WIDTH-1:0] AXI_ARLOCK; 77 | logic [C_AXI_CACHE_WIDTH-1:0] AXI_ARCACHE; 78 | logic [C_AXI_PROT_WIDTH-1:0] AXI_ARPROT; 79 | logic [C_AXI_QOS_WIDTH-1:0] AXI_ARQOS; 80 | logic [C_AXI_VALID_WIDTH-1:0] AXI_ARVALID; 81 | logic [C_AXI_READY_WIDTH-1:0] AXI_ARREADY; 82 | 83 | // AXI data read phase 84 | logic [C_AXI_ID_WIDTH-1:0] AXI_RID; 85 | logic [C_AXI_DATA_WIDTH-1:0] AXI_RDATA; 86 | logic [C_AXI_RESP_WIDTH-1:0] AXI_RRESP; 87 | logic [C_AXI_LAST_WIDTH-1:0] AXI_RLAST; 88 | logic [C_AXI_VALID_WIDTH-1:0] AXI_RVALID; 89 | logic [C_AXI_READY_WIDTH-1:0] AXI_RREADY; 90 | 91 | // write data count 92 | // read data count 93 | 94 | /*------------------------------------------- 95 | // modport for each module type, like master, slave... 96 | // please add your modports here 97 | ---------------------------------------------*/ 98 | 99 | // modports in slave interfaces 100 | modport Slave (); 101 | 102 | // modports in master interfaces 103 | modport Master (); 104 | 105 | // modports in monitor interface 106 | modport Monitor (); 107 | 108 | /*----------------------------------------------- 109 | // Assertions -> axi_assertioms.sv 110 | // please add your assertion rules here 111 | ------------------------------------------------*/ 112 | 113 | always @(negedge AXI_ACLK) 114 | begin 115 | 116 | // write address must not be X or Z during address write phase 117 | assertWriteAddrUnknown:assert property ( 118 | disable iff(!has_checks) 119 | ($onehot(AXI_AWVALID) && $onehot(AXI_AWREADY) |-> !$isunknown(AXI_AWADDR))) 120 | else 121 | $error({$psprintf("ERR_AXI_AWADDR %s went to X or Z during address write phase when AXI_AWVALID=1", name)}); 122 | 123 | // write data must not be X or Z during data write phase 124 | assertWriteDataUnknown:assert property ( 125 | disable iff(!has_checks) 126 | ($onehot(AXI_WVALID) && $onehot(AXI_WREADY) |-> !$isunknown(AXI_WDATA))) 127 | else 128 | $error({$psprintf("ERR_AXI_WDATA %s went to X or Z during data write phase when AXI_WVALID=1", name)}); 129 | 130 | // write resp must not be X or Z during resp write phase 131 | assertWriteRespUnKnown:assert property ( 132 | disable iff(!has_checks) 133 | ($onehot(AXI_BVALID) && $onehot(AXI_BREADY) |-> !$isunknown(AXI_BRESP))) 134 | else 135 | $error({$psprintf("ERR_AXI_BRESP %s went to X or Z during response write phase when AXI_BVALID=1", name)}); 136 | 137 | // read address must not be X or Z during address read phase 138 | assertReadAddrUnKnown:assert property ( 139 | disable iff(!has_checks) 140 | ($onehot(AXI_ARVALID) && $onehot(AXI_ARREADY) |-> !$isunknown(AXI_ARADDR))) 141 | else 142 | $error({$psprintf("ERR_AXI_ARADDR %s went to X or Z during address read phase when AXI_ARVALID=1", name)}); 143 | 144 | // read data must not be X or Z during read data phase 145 | assertReadDataUnKnown:assert property ( 146 | disable iff(!has_checks) 147 | ($onehot(AXI_RVALID) && $onehot(AXI_RREADY) |-> !$isunknown(AXI_RDATA))) 148 | else 149 | $error({$psprintf("ERR_AXI_AWDATA %s went to X or Z during data read phase when AXI_RVALID=1", name)}); 150 | 151 | // assert each pin has value not unknown 152 | 153 | end 154 | 155 | endinterface : AXI_vif 156 | 157 | -------------------------------------------------------------------------------- /sv/sequence_libs/axi_based_seq_lib.sv: -------------------------------------------------------------------------------- 1 | `ifndef AXI_BASED_SEQ_LIB_SV 2 | `define AXI_BASED_SEQ_LIB_SV 3 | 4 | //------------------------------------------------------------------------------ 5 | // SEQUENCE: base_seq 6 | //------------------------------------------------------------------------------ 7 | class AXI_base_seq extends uvm_sequence #(AXI_transfer); 8 | 9 | function new(string name="axi_base_seq"); 10 | super.new(name); 11 | endfunction 12 | 13 | `uvm_object_utils_begin(AXI_base_seq) 14 | `uvm_object_utils_end 15 | 16 | endclass : AXI_base_seq 17 | 18 | `endif // AXI_BASED_SEQ_LIB_SV 19 | 20 | -------------------------------------------------------------------------------- /sv/sequence_libs/axi_master_based_seq_lib.sv: -------------------------------------------------------------------------------- 1 | `ifndef AXI_MASTER_BASED_SEQ_LIB_SV 2 | `define AXI_MASTER_BASED_SEQ_LIB_SV 3 | 4 | class AXI_master_base_seq extends AXI_base_seq; 5 | 6 | function new(string name="axi_master_base_seq"); 7 | super.new(name); 8 | endfunction 9 | 10 | `uvm_object_utils_begin(AXI_master_base_seq) 11 | `uvm_object_utils_end 12 | 13 | `uvm_declare_p_sequencer(AXI_master_sequencer) 14 | 15 | // Use a base sequence to raise/drop objections if this is a default sequence 16 | virtual task pre_body(); 17 | if (starting_phase != null) 18 | starting_phase.raise_objection(this, {"Running sequence '", 19 | get_full_name(), "'"}); 20 | endtask 21 | 22 | virtual task post_body(); 23 | if (starting_phase != null) 24 | starting_phase.drop_objection(this, {"Completed sequence '", 25 | get_full_name(), "'"}); 26 | endtask 27 | 28 | endclass : AXI_master_base_seq 29 | 30 | `endif // AXI_MASTER_BASED_SEQ_LIB_SV 31 | 32 | -------------------------------------------------------------------------------- /sv/sequence_libs/axi_master_read_seq_lib.sv: -------------------------------------------------------------------------------- 1 | 2 | `ifndef AXI_MASTER_READ_SEQ_LIB_SV 3 | `define AXI_MASTER_READ_SEQ_LIB_SV 4 | 5 | //--------------------------------------------- 6 | // SEQUENCE: read_seq 7 | // create read trx 8 | //--------------------------------------------- 9 | class AXI_master_read_seq extends AXI_transfer; 10 | 11 | AXI_transfer m_trx; 12 | 13 | function new(string name ="axi_master_read_seq", 14 | int unsigned addr = 32'h0000_10000, 15 | byte_enum ibyte = BYTE_4, 16 | len_enum len = LEN_4, 17 | burst_enum burst = INCR, 18 | int unsigned id = 1, 19 | int unsigned addr_delay = 0, 20 | int unsigned data_delay = 0 21 | ); 22 | 23 | super.new(name); 24 | $cast(m_trx, super); 25 | 26 | m_trx.rw = READ; 27 | m_trx.addr = addr; 28 | m_trx.size = ibyte; 29 | m_trx.len = len; 30 | m_trx.burst = burst; 31 | m_trx.id = id; 32 | m_trx.addr_rd_delay = addr_delay; 33 | m_trx.data_rd_delay = data_delay; 34 | 35 | endfunction 36 | 37 | `uvm_object_utils(AXI_master_read_seq) 38 | 39 | endclass : AXI_master_read_seq 40 | 41 | `endif // AXI_MASTER_READ_SEQ_LIB_SV 42 | -------------------------------------------------------------------------------- /sv/sequence_libs/axi_master_write_seq_lib.sv: -------------------------------------------------------------------------------- 1 | 2 | `ifndef AXI_MASTER_WRITE_SEQ_LIB_SV 3 | `define AXI_MASTER_WRITE_SEQ_LIB_SV 4 | 5 | //--------------------------------------------- 6 | // SEQUENCE: write_seq 7 | // create write trx 8 | //--------------------------------------------- 9 | class AXI_master_write_seq extends AXI_transfer; 10 | 11 | AXI_transfer m_trx; 12 | 13 | function new(string name = "axi_master_write_seq", 14 | int unsigned addr = 32'h0000_1000, 15 | byte_enum ibyte = BYTE_4, 16 | len_enum len = LEN_4, 17 | burst_enum burst = INCR, 18 | int unsigned id = 1, 19 | int unsigned data[$] = { 32'h0000_0001, 20 | 32'h0000_0002, 21 | 32'h0000_0003, 22 | 32'h0000_0004 }, 23 | int unsigned strb[$] = { 4'b1111, 24 | 4'b1111, 25 | 4'b1111, 26 | 4'b1111 }, 27 | int unsigned addr_delay = 0, 28 | int unsigned data_delay = 0, 29 | int unsigned resp_delay = 0 30 | ); 31 | 32 | super.new(name); 33 | $cast(m_trx, super); 34 | 35 | m_trx.rw = WRITE; 36 | m_trx.addr = addr; 37 | m_trx.size = ibyte; 38 | m_trx.len = len; 39 | m_trx.burst = burst; 40 | m_trx.id = id; 41 | // m_trx.data_bytes = 4; 42 | m_trx.data = data; 43 | m_trx.strb = strb; 44 | m_trx.addr_wt_delay = addr_delay; 45 | m_trx.data_wt_delay = data_delay; 46 | m_trx.resp_wt_delay = resp_delay; 47 | 48 | endfunction 49 | 50 | `uvm_object_utils(AXI_master_write_seq) 51 | 52 | endclass : AXI_master_write_seq 53 | 54 | `endif // AXI_MASTER_WRITE_SEQ_LIB_SV 55 | -------------------------------------------------------------------------------- /sv/sequence_libs/axi_seq_lib_pkg.sv: -------------------------------------------------------------------------------- 1 | /*-------------------------------------- 2 | // AXI SEQ LIB PKG 3 | // file : axi_pkg.sv 4 | // author : SeanChen 5 | // date : 2013/05/06 6 | // notes 7 | ---------------------------------------*/ 8 | 9 | 10 | `ifndef AXI_SEQ_LIB_PKG_SV 11 | `define AXI_SEQ_LIB_PKG_SV 12 | 13 | package axi_seq_lib_pkg; 14 | 15 | // Import the UVM class library and UVM automation macros 16 | import uvm_pkg::*; 17 | `include "uvm_macros.svh" 18 | 19 | `include "axi_pkg.sv" 20 | import axi_pkg::*; 21 | 22 | `include "axi_based_seq_lib.sv" 23 | `include "axi_master_based_seq_lib.sv" 24 | `include "axi_slave_based_seq_lib.sv" 25 | `include "axi_master_read_seq_lib.sv" 26 | `include "axi_master_write_seq_lib.sv" 27 | 28 | endpackage : axi_seq_lib_pkg 29 | 30 | `endif // AXI_SEQ_LIB_PKG_SV 31 | -------------------------------------------------------------------------------- /sv/sequence_libs/axi_slave_based_seq_lib.sv: -------------------------------------------------------------------------------- 1 | `ifndef AXI_SLAVE_BASED_SEQ_LIB_SV 2 | `define AXI_SLAVE_BASED_SEQ_LIB_SV 3 | 4 | class AXI_slave_base_seq extends AXI_base_seq; 5 | 6 | function new(string name="axi_slave_base_seq"); 7 | super.new(name); 8 | endfunction 9 | 10 | `uvm_object_utils_begin(AXI_slave_base_seq) 11 | `uvm_object_utils_end 12 | 13 | `uvm_declare_p_sequencer(AXI_slave_sequencer) 14 | 15 | // Use a base sequence to raise/drop objections if this is a default sequence 16 | virtual task pre_body(); 17 | if (starting_phase != null) 18 | starting_phase.raise_objection(this, {"Running sequence '", 19 | get_full_name(), "'"}); 20 | endtask 21 | 22 | virtual task post_body(); 23 | if (starting_phase != null) 24 | starting_phase.drop_objection(this, {"Completed sequence '", 25 | get_full_name(), "'"}); 26 | endtask 27 | 28 | endclass : AXI_slave_base_seq 29 | 30 | `endif // AXI_SLAVE_BASED_SEQ_LIB_SV 31 | 32 | -------------------------------------------------------------------------------- /v/axi_slave.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/10ps 2 | 3 | module axi_slave 4 | #( 5 | parameter AXI_ID_WIDTH = 4 6 | ) 7 | ( 8 | clk, 9 | rstn, 10 | 11 | // AXI write address channel 12 | i_awaddr, 13 | i_awid, 14 | i_awlen, 15 | i_awvalid, 16 | o_awready, 17 | 18 | // AXI write data channel 19 | i_wdata, 20 | i_wid, 21 | i_wstrb, 22 | i_wlast, 23 | i_wvalid, 24 | o_wready, 25 | o_bresp, 26 | o_bid, 27 | o_bvalid, 28 | i_bready, 29 | 30 | // AXI read address channel 31 | i_araddr, 32 | i_arid, 33 | i_arlen, 34 | i_arvalid, 35 | o_arready, 36 | 37 | // AXI read data channel 38 | o_rdata, 39 | o_rid, 40 | o_rresp, 41 | o_rlast, 42 | o_rvalid, 43 | i_rready 44 | ); 45 | 46 | input clk; 47 | input rstn; 48 | 49 | // AXI write address channel 50 | input [31:0] i_awaddr; 51 | input [AXI_ID_WIDTH-1:0] i_awid; 52 | input [3:0] i_awlen; 53 | input i_awvalid; 54 | output o_awready; 55 | 56 | // AXI write data channel 57 | input [31:0] i_wdata; 58 | input [AXI_ID_WIDTH-1:0] i_wid; 59 | input [3:0] i_wstrb; 60 | input i_wlast; 61 | input i_wvalid; 62 | output o_wready; 63 | output [AXI_ID_WIDTH-1:0] o_bid; 64 | output [1:0] o_bresp; 65 | output o_bvalid; 66 | input i_bready; 67 | 68 | // AXI read address channel 69 | input [31:0] i_araddr; 70 | input [AXI_ID_WIDTH-1:0] i_arid; 71 | input [3:0] i_arlen; 72 | input i_arvalid; 73 | output o_arready; 74 | 75 | // AXI read data channel 76 | output [31:0] o_rdata; 77 | output [AXI_ID_WIDTH-1:0] o_rid; 78 | output [1:0] o_rresp; 79 | output o_rlast; 80 | output o_rvalid; 81 | input i_rready; 82 | 83 | parameter ST_R_IDLE = 3'd0; 84 | parameter ST_R_PRE1 = 3'd1; 85 | parameter ST_R_PRE2 = 3'd2; 86 | parameter ST_R_PRE3 = 3'd3; 87 | parameter ST_R_READ = 3'd4; 88 | parameter ST_R_END = 3'd5; 89 | 90 | parameter ST_W_IDLE = 3'd0; 91 | parameter ST_W_PRE1 = 3'd1; 92 | parameter ST_W_PRE2 = 3'd2; 93 | parameter ST_W_PRE3 = 3'd3; 94 | parameter ST_W_WRITE = 3'd4; 95 | parameter ST_W_END = 3'd5; 96 | 97 | reg [2:0] r_cs; 98 | reg [2:0] r_ns; 99 | reg [2:0] w_cs; 100 | reg [2:0] w_ns; 101 | 102 | reg [3:0] rdcnt; 103 | reg [31:0] araddr; 104 | reg [3:0] arlen; 105 | reg [AXI_ID_WIDTH-1:0] arid; 106 | 107 | reg [3:0] wdcnt; 108 | reg [31:0] awaddr; 109 | reg [3:0] awlen; 110 | reg [AXI_ID_WIDTH-1:0] awid; 111 | 112 | reg [5:0] axi_wait_cnt_0; 113 | reg [5:0] axi_wait_cnt_1; 114 | reg [5:0] axi_wait_num_0; 115 | reg [5:0] axi_wait_num_1; 116 | reg [31:0] rdn_num_0; 117 | reg [31:0] rdn_num_1; 118 | 119 | reg [31:0] o_rdata; 120 | reg [31:0] mem[63:0]; 121 | 122 | initial begin 123 | $mem_alloc(); 124 | end 125 | 126 | always@(posedge clk or negedge rstn) begin 127 | if (!rstn) begin 128 | rdn_num_0 <= 32'd0; 129 | rdn_num_1 <= 32'd0; 130 | end else begin 131 | rdn_num_0 <= $random; 132 | rdn_num_1 <= $random; 133 | end 134 | end 135 | 136 | always@(posedge clk or negedge rstn) begin 137 | if (!rstn) begin 138 | axi_wait_cnt_0 <= 6'd0; 139 | axi_wait_num_0 <= 6'd8; 140 | end else if (i_arvalid & o_arready) begin 141 | axi_wait_cnt_0 <= 6'd0; 142 | axi_wait_num_0 <= rdn_num_0[5:0]; 143 | end else if (i_arvalid & !o_arready) begin 144 | if (axi_wait_cnt_0==axi_wait_num_0) begin 145 | axi_wait_cnt_0 <= 5'd0; 146 | end else begin 147 | axi_wait_cnt_0 <= axi_wait_cnt_0 + 1'b1; 148 | end 149 | end 150 | end 151 | 152 | always@(posedge clk or negedge rstn) begin 153 | if (!rstn) begin 154 | axi_wait_cnt_1 <= 6'd0; 155 | axi_wait_num_1 <= 6'd8; 156 | end else if (i_awvalid & o_awready) begin 157 | axi_wait_cnt_1 <= 6'd0; 158 | axi_wait_num_1 <= rdn_num_1[5:0]; 159 | end else if (i_awvalid & !o_awready) begin 160 | if (axi_wait_cnt_1==axi_wait_num_1) begin 161 | axi_wait_cnt_1 <= 5'd0; 162 | end else begin 163 | axi_wait_cnt_1 <= axi_wait_cnt_1 + 1'b1; 164 | end 165 | end 166 | end 167 | 168 | //------------------------------------------------------------------------------------------------ 169 | `ifdef AXI_BUSY 170 | assign o_arready = (r_cs==ST_R_IDLE) & (axi_wait_cnt_0==axi_wait_num_0); 171 | `else 172 | assign o_arready = (r_cs==ST_R_IDLE); 173 | `endif 174 | 175 | assign o_rresp = 2'b00; 176 | assign o_rvalid = (r_cs==ST_R_READ); 177 | assign o_rlast = o_rvalid & (rdcnt==arlen); 178 | assign o_rid = arid; 179 | 180 | always@(posedge clk or negedge rstn) begin 181 | if (!rstn) begin 182 | r_cs <= ST_R_IDLE; 183 | end else begin 184 | r_cs <= r_ns; 185 | end 186 | end 187 | 188 | always@(*) begin 189 | 190 | r_ns = r_cs; 191 | 192 | case (r_cs) 193 | ST_R_IDLE : r_ns = (i_arvalid & o_arready) ? ST_R_PRE1 : r_cs; 194 | ST_R_PRE1 : r_ns = ST_R_PRE2; 195 | ST_R_PRE2 : r_ns = ST_R_PRE3; 196 | ST_R_PRE3 : r_ns = ST_R_READ; 197 | ST_R_READ : r_ns = (o_rvalid & i_rready & rdcnt==arlen) ? ST_R_END : r_cs; 198 | ST_R_END : r_ns = ST_R_IDLE; 199 | endcase 200 | end 201 | 202 | 203 | always@(posedge clk or negedge rstn) begin 204 | if (!rstn) begin 205 | rdcnt <= 4'd0; 206 | end else if (o_rvalid & i_rready) begin 207 | if (rdcnt==arlen) begin 208 | rdcnt <= 4'd0; 209 | end else begin 210 | rdcnt <= rdcnt + 1'b1; 211 | end 212 | end 213 | end 214 | 215 | always@(posedge clk) begin 216 | if (i_arvalid & o_arready) begin 217 | araddr <= i_araddr; 218 | arlen <= i_arlen; 219 | arid <= i_arid; 220 | end 221 | end 222 | 223 | always@(posedge clk) begin 224 | if ((r_cs==ST_R_PRE3) || (r_cs==ST_R_READ)) begin 225 | $mem_read(araddr,o_rdata); 226 | araddr <= araddr + 4; 227 | end 228 | end 229 | 230 | //------------------------------------------------------------------------------------------------ 231 | always@(posedge clk or negedge rstn) begin 232 | if (!rstn) begin 233 | w_cs <= ST_W_IDLE; 234 | end else begin 235 | w_cs <= w_ns; 236 | end 237 | end 238 | 239 | always@(*) begin 240 | 241 | w_ns = w_cs; 242 | 243 | case (w_cs) 244 | ST_W_IDLE : w_ns = (i_awvalid & o_awready) ? ST_W_PRE1 : w_cs; 245 | ST_W_PRE1 : w_ns = ST_W_PRE2; 246 | ST_W_PRE2 : w_ns = ST_W_PRE3; 247 | ST_W_PRE3 : w_ns = ST_W_WRITE; 248 | ST_W_WRITE : w_ns = (i_wvalid & o_wready & wdcnt==awlen) ? ST_W_END : w_cs; 249 | ST_W_END : w_ns = (o_bvalid & i_bready) ? ST_W_IDLE : w_cs; 250 | endcase 251 | end 252 | 253 | always@(posedge clk or negedge rstn) begin 254 | if (!rstn) begin 255 | wdcnt <= 4'd0; 256 | end else if (i_wvalid & o_wready) begin 257 | if (wdcnt==awlen) begin 258 | wdcnt <= 4'd0; 259 | end else begin 260 | wdcnt <= wdcnt + 1'b1; 261 | end 262 | end 263 | end 264 | 265 | always@(posedge clk) begin 266 | if (i_awvalid & o_awready) begin 267 | awaddr <= i_awaddr; 268 | awlen <= i_awlen;; 269 | awid <= i_awid; 270 | end 271 | end 272 | 273 | always@(posedge clk) begin 274 | integer t; 275 | if (i_wvalid & o_wready) begin 276 | $mem_write(awaddr,i_wstrb,i_wdata); 277 | awaddr <= awaddr + 4; 278 | end 279 | end 280 | 281 | // for error checking 282 | always@(posedge clk) begin 283 | if (i_wvalid & o_wready) begin 284 | if (wdcnt==awlen & !i_wlast) begin 285 | $display("[FAIL]: awlen does not match with wlast"); 286 | $finish; 287 | end 288 | if (wdcnt!=awlen & i_wlast) begin 289 | $display("[FAIL]: awlen does not match with wlast"); 290 | $finish; 291 | end 292 | end 293 | end 294 | 295 | `ifdef AXI_BUSY 296 | assign o_awready = (w_cs==ST_W_IDLE) & (axi_wait_cnt_1==axi_wait_num_1); 297 | `else 298 | assign o_awready = (w_cs==ST_W_IDLE); 299 | `endif 300 | 301 | assign o_wready = (w_cs==ST_W_WRITE); 302 | assign o_bresp = 2'b00; 303 | assign o_bid = awid; 304 | assign o_bvalid = (w_cs==ST_W_END); 305 | 306 | endmodule 307 | --------------------------------------------------------------------------------