diff --git a/src/axi/axi_manager_driver.sv b/src/axi/axi_manager_driver.sv index 6ff7dff..713780a 100644 --- a/src/axi/axi_manager_driver.sv +++ b/src/axi/axi_manager_driver.sv @@ -2,6 +2,10 @@ // AXI Manager Driver // ---------------------------------------------------------------------- class axi_manager_driver extends axi_driver; + axi_transaction txn_trk[axi_transaction_id_t]; + + semaphore write_request_sem; + semaphore read_request_sem; `uvm_component_utils(axi_manager_driver) @@ -9,6 +13,9 @@ class axi_manager_driver extends axi_driver; // Constructor function new(string name = "axi_manager_driver", uvm_component parent = null); super.new(name, parent); + + write_request_sem = new(1); + read_request_sem = new(1); endfunction // -------------------------------------------------- @@ -33,7 +40,7 @@ class axi_manager_driver extends axi_driver; `uvm_info("run_phase", $sformatf("Starting to drive transactions for %s (rst_n = %0b)", get_full_name(), m_if.ARESETn), UVM_LOW) - // Drive transactions until reset is asserted + // Drive transactions until reset is asserted while (m_if.ARESETn != 0) begin uvm_sequence_item txn; axi_transaction req; @@ -45,7 +52,14 @@ class axi_manager_driver extends axi_driver; if (!$cast(req, txn)) begin `uvm_fatal("drive_txn", "Invalid transaction type") end - drive_txn(req); + while (txn_trk.size() > (3-1)) begin + `uvm_info("run_phase", $sformatf("Waiting for next transaction Txn-trk size=%0d to be under 3", txn_trk.size()), UVM_LOW) + @(txn_trk.size()); + `uvm_info("run_phase", $sformatf("Txn-trk size changed to %0d to be under 3", txn_trk.size()), UVM_LOW) + end + fork + drive_txn(req); + join_none seq_item_port.item_done(); end end @@ -54,7 +68,12 @@ class axi_manager_driver extends axi_driver; // ------------------------------------------------------------ task drive_txn(axi_transaction req); int dly = cfg.get_pre_transaction_delay(); - `uvm_info("drive_txn", $sformatf("Driving AXI transaction:\n%s", req.sprint()), UVM_LOW) + axi_transaction_id_t tid = req.get_tid(); + + `uvm_info("drive_txn", $sformatf("Driving AXI transaction ID=%s:\n%s", + req.show_tid(), req.sprint()), UVM_LOW) + + txn_trk[tid] = req; // Pre transaction delay `uvm_info("drive_txn", $sformatf("Waiting pre_transaction_delay = %0d cycles", @@ -69,39 +88,120 @@ class axi_manager_driver extends axi_driver; `uvm_error("drive_txn", $sformatf("Unknown transaction type: %s", req.txn_type.name())) end `uvm_info("drive_txn", $sformatf("Driving AXI transaction done."), UVM_LOW) + + finish_tr(req); + + txn_trk.delete(tid); endtask // ------------------------------------------------------------ // Drive a write transaction task drive_write_txn(axi_transaction req); + bit done = 0; + bit a_done = 0; + bit w_done = 0; + bit b_done = 0; + + `uvm_info("drive_write_txn", $sformatf("[%s] Waiting for semaphore to drive", + req.show_tag()), UVM_LOW) + write_request_sem.get(); + + // Drive address channel information + // TODO: Sometimes drive after delay + // TODO: Sometimes drive after waiting for AWREADY @(posedge m_if.ACLK); m_if.AWVALID = 1; m_if.AWADDR = req.addr; - @(posedge m_if.ACLK); + m_if.AWID = 0; + + // Drive data channel information + // TODO: Sometimes drive after delay + // TODO: Sometimes drive after waiting for WREADY + m_if.WVALID = 1; m_if.WDATA = req.data; m_if.WSTRB = req.strb; - while (m_if.AWREADY != 1) begin + // Wait for write response + @(posedge m_if.ACLK); + m_if.BREADY = 1; + while (!done) begin @(posedge m_if.ACLK); + + if (m_if.AWREADY == 1) begin + a_done = 1; + m_if.AWVALID = 0; + end + if (m_if.WREADY == 1) begin + w_done = 1; + m_if.WVALID = 0; + end + + if (m_if.BVALID == 1) begin + b_done = 1; + m_if.BREADY = 0; + end + + // Write is done when Address, Data and Response phases are done + done = (a_done && w_done && b_done); + `uvm_info("drive_write_txn", $sformatf("[%s] Waiting for transaction to be done (%b - a%b w%b b%b)", + req.show_tag(), done, a_done, w_done, b_done), UVM_LOW) end - `uvm_info("drive_write_txn", $sformatf("Address phase accepted (AWREADY=1)"), UVM_LOW) - m_if.AWVALID = 0; - `uvm_info("drive_write_txn", $sformatf("Done."), UVM_LOW) + + write_request_sem.put(); endtask // ------------------------------------------------------------ // Drive a read transaction task drive_read_txn(axi_transaction req); + bit done = 0; + bit a_done = 0; + bit r_done = 0; + + `uvm_info("drive_read_txn", $sformatf("[%s] Waiting for semaphore to drive", + req.show_tag()), UVM_LOW) + read_request_sem.get(); + + // Drive address channel information + // TODO: Sometimes drive after delay + // TODO: Sometimes drive after waiting for AWREADY @(posedge m_if.ACLK); m_if.ARVALID = 1; m_if.ARADDR = req.addr; - @(posedge m_if.ACLK); + m_if.ARID = 0; - while (m_if.ARREADY != 1) begin + // Wait for write response + @(posedge m_if.ACLK); + m_if.RREADY = 1; + while (!done) begin @(posedge m_if.ACLK); + + if (m_if.ARREADY == 1) begin + a_done = 1; + m_if.ARVALID = 0; + end + + if (m_if.RVALID == 1) begin + r_done = 1; + m_if.RREADY = 0; + end + + // Write is done when Address, Data and Response phases are done + done = (a_done && r_done); + `uvm_info("drive_read_txn", $sformatf("[%s] Waiting for transaction to be done (Data=0x%h) (%b - a%b r%b)", + req.show_tag(), m_if.RDATA, done, a_done, r_done), UVM_LOW) end - `uvm_info("drive_read_txn", $sformatf("Address phase accepted (ARREADY=1)"), UVM_LOW) - m_if.ARVALID = 0; - `uvm_info("drive_read_txn", $sformatf("Done."), UVM_LOW) + + read_request_sem.put(); endtask + + // ------------------------------------------------------------ + // Mark transaction done + function void finish_tr(axi_transaction txn); + uvm_event finished_event; + + finished_event = txn.get_event_pool().get("finished"); + finished_event.trigger(); + `uvm_info("finish_tr", $sformatf("[%s] Marked transaction done", + txn.show_tag()), UVM_LOW) + endfunction endclass : axi_manager_driver diff --git a/src/axi/axi_monitor.sv b/src/axi/axi_monitor.sv index b80c503..c004adb 100644 --- a/src/axi/axi_monitor.sv +++ b/src/axi/axi_monitor.sv @@ -71,13 +71,13 @@ class axi_monitor extends uvm_monitor; // #(axi_transaction); forever begin @(posedge mon_if.ACLK); if (mon_if.ARESETn != 0) begin - if (mon_if.AWVALID == 1) begin - $fwrite(trk_file, "AXI WRITE - ADDR=0x%h DATA=0x%h STRB=%h\n", - mon_if.AWADDR, mon_if.WDATA, mon_if.WSTRB); + if ((mon_if.AWVALID == 1) && (mon_if.AWREADY == 1)) begin + $fwrite(trk_file, "%10t: AXI WRITE - ADDR=0x%h DATA=0x%h STRB=%h\n", + $time, mon_if.AWADDR, mon_if.WDATA, mon_if.WSTRB); end - if (mon_if.ARVALID == 1) begin - $fwrite(trk_file, "AXI READ - ADDR=0x%h\n", - mon_if.ARADDR); + if ((mon_if.ARVALID == 1) && (mon_if.ARREADY == 1)) begin + $fwrite(trk_file, "%10t: AXI READ - ADDR=0x%h\n", + $time, mon_if.ARADDR); end end end diff --git a/src/axi/axi_subordinate_driver.sv b/src/axi/axi_subordinate_driver.sv index 66c63a9..166fec4 100644 --- a/src/axi/axi_subordinate_driver.sv +++ b/src/axi/axi_subordinate_driver.sv @@ -1,5 +1,10 @@ // ---------------------------------------------------------------------- class axi_subordinate_driver extends axi_driver; + int rd_txn_id = 0; + int wr_txn_id = 0; + + semaphore write_request_sem; + semaphore read_request_sem; `uvm_component_utils(axi_subordinate_driver) @@ -7,6 +12,9 @@ class axi_subordinate_driver extends axi_driver; // Constructor function new(string name = "axi_subordinate_driver", uvm_component parent = null); super.new(name, parent); + + write_request_sem = new(1); + read_request_sem = new(1); endfunction // -------------------------------------------------- @@ -19,21 +27,22 @@ class axi_subordinate_driver extends axi_driver; end forever begin - @(posedge s_if.ARESETn); + @(s_if.ARESETn or s_if.ACLK); - while (s_if.ARESETn != 0) begin - @(posedge s_if.ACLK); + if (s_if.ARESETn == 0) continue; - // Following could happen on the same cycle + if (s_if.ACLK == 1) begin if (s_if.AWVALID == 1'b1) begin - respond_to_write_txn(); + fork + respond_to_write_txn(); + join_none end if (s_if.ARVALID == 1'b1) begin - respond_to_read_txn(); + fork + respond_to_read_txn(); + join_none end - - @(negedge s_if.ACLK); end end endtask @@ -43,22 +52,73 @@ class axi_subordinate_driver extends axi_driver; axi_transaction req; int dly = cfg.get_pre_response_delay(); + while (s_if.AWVALID != 1) begin + `uvm_info("respond_to_write_txn", $sformatf("Waiting for AWVALID to set (%0b)", + s_if.AWVALID), UVM_LOW) + @(posedge s_if.ACLK); + end + + if (!write_request_sem.try_get()) begin + `uvm_info("respond_to_write_txn", $sformatf("Already taken write semaphore"), UVM_LOW) + return; + end + // `uvm_info("respond_to_write_txn", $sformatf("Waiting for write semaphore"), UVM_LOW) + // write_request_sem.get(); + `uvm_info("respond_to_write_txn", $sformatf("Got write semaphore"), UVM_LOW) + req = axi_transaction::type_id::create("req"); + req.set_sequence_id(1); + req.set_transaction_id(wr_txn_id); + wr_txn_id += 1; req.txn_type = AXI_WRITE; - req.addr = s_if.AWADDR; - req.data = s_if.WDATA; - req.strb = s_if.WSTRB; // Pre response delay - `uvm_info("drive_txn", $sformatf("Waiting pre_response_delay = %0d cycles", - dly), UVM_LOW) + `uvm_info("respond_to_write_txn", $sformatf("[%s] Waiting pre_response_delay = %0d cycles", + req.show_tag(), dly), UVM_LOW) repeat(dly) @(posedge s_if.ACLK); - `uvm_info("respond_to_write_txn", $sformatf("Responding to AXI write transaction:\n%s", req.sprint()), UVM_LOW) + `uvm_info("respond_to_write_txn", $sformatf("[%s] Responding to AXI write transaction:\n%s", + req.show_tag(), req.sprint()), UVM_LOW) s_if.AWREADY = 1'b1; + req.addr = s_if.AWADDR; @(posedge s_if.ACLK); + while (s_if.AWVALID != 0) begin + @(posedge s_if.ACLK); + end s_if.AWREADY = 1'b0; + + `uvm_info("respond_to_write_txn", $sformatf("[%s] Request phase done. Waiting data phase", + req.show_tag()), UVM_LOW) + while (s_if.WVALID != 1) begin + `uvm_info("respond_to_write_txn", $sformatf("[%s] Waiting for WVALID to set (%0b)", + req.show_tag(), s_if.WVALID), UVM_LOW) + @(posedge s_if.ACLK); + end + s_if.WREADY = 1'b1; + req.data = s_if.WDATA; + req.strb = s_if.WSTRB; + @(posedge s_if.ACLK); + while (s_if.WVALID != 0) begin + `uvm_info("respond_to_write_txn", $sformatf("[%s] Waiting for WVALID to clear (%0b)", + req.show_tag(), s_if.WVALID), UVM_LOW) + @(posedge s_if.ACLK); + end + s_if.WREADY = 1'b0; + + `uvm_info("respond_to_write_txn", $sformatf("[%s] Data phase done. Waiting response phase", + req.show_tag()), UVM_LOW) + s_if.BVALID = 1; + @(posedge s_if.ACLK); + while (s_if.BREADY != 1) begin + `uvm_info("respond_to_write_txn", $sformatf("[%s] Waiting for BREADY to set (%0b)", + req.show_tag(), s_if.BREADY), UVM_LOW) + @(posedge s_if.ACLK); + end + s_if.BVALID = 0; + `uvm_info("respond_to_write_txn", $sformatf("[%s] Response phase done", + req.show_tag()), UVM_LOW) + write_request_sem.put(); endtask // ------------------------------------------------------------ @@ -66,19 +126,57 @@ class axi_subordinate_driver extends axi_driver; axi_transaction req; int dly = cfg.get_pre_response_delay(); + while (s_if.ARVALID != 1) begin + `uvm_info("respond_to_read_txn", $sformatf("Waiting for ARVALID to be set (%0b)", + s_if.ARVALID), UVM_LOW) + @(posedge s_if.ACLK); + end + + if (!read_request_sem.try_get()) begin + `uvm_info("respond_to_read_txn", $sformatf("Already taken read semaphore"), UVM_LOW) + return; + end + // `uvm_info("respond_to_read_txn", $sformatf("Waiting for read semaphore"), UVM_LOW) + // read_request_sem.get(); + `uvm_info("respond_to_read_txn", $sformatf("Got read semaphore"), UVM_LOW) + req = axi_transaction::type_id::create("req"); + req.set_sequence_id(0); + req.set_transaction_id(rd_txn_id); + rd_txn_id += 1; req.txn_type = AXI_READ; req.addr = s_if.ARADDR; // Pre response delay - `uvm_info("respond_to_write_txn", $sformatf("Waiting pre_response_delay = %0d cycles", + `uvm_info("respond_to_read_txn", $sformatf("Waiting pre_response_delay = %0d cycles", dly), UVM_LOW) repeat(dly) @(posedge s_if.ACLK); - `uvm_info("respond_to_write_txn", $sformatf("Responding to AXI write transaction:\n%s", req.sprint()), UVM_LOW) + `uvm_info("respond_to_read_txn", $sformatf("Responding to AXI read transaction:\n%s", + req.sprint()), UVM_LOW) s_if.ARREADY = 1'b1; @(posedge s_if.ACLK); + while (s_if.ARVALID != 0) begin + `uvm_info("respond_to_read_txn", $sformatf("[%s] Waiting for ARVALID to clear (%0b)", + req.show_tag(), s_if.ARVALID), UVM_LOW) + @(posedge s_if.ACLK); + end s_if.ARREADY = 1'b0; + + `uvm_info("respond_to_read_txn", $sformatf("[%s] Data phase done. Waiting response phase", + req.show_tag()), UVM_LOW) + s_if.RVALID = 1; + @(posedge s_if.ACLK); + while (s_if.RREADY != 1) begin + `uvm_info("respond_to_read_txn", $sformatf("[%s] Waiting for RREADY to set (%0b)", + req.show_tag(), s_if.RREADY), UVM_LOW) + @(posedge s_if.ACLK); + end + s_if.RVALID = 0; + `uvm_info("respond_to_read_txn", $sformatf("[%s] Response phase done", + req.show_tag()), UVM_LOW) + + read_request_sem.put(); endtask endclass : axi_subordinate_driver diff --git a/src/axi/axi_transaction.sv b/src/axi/axi_transaction.sv index c474ae1..60058ec 100644 --- a/src/axi/axi_transaction.sv +++ b/src/axi/axi_transaction.sv @@ -1,16 +1,20 @@ // ---------------------------------------------------------------------- class axi_transaction extends uvm_sequence_item; // Declare AXI transaction fields - rand axi_transaction_type_t txn_type; // Transaction type (read/write) - rand bit [`ADDR_WIDTH-1:0] addr; // Address - rand bit [`DATA_WIDTH-1:0] data; // Data - rand bit [`DATA_WIDTH_DIV_8-1:0] strb; // Byte enable + rand axi_transaction_type_t txn_type; // Transaction type (read/write) + rand bit [`ADDR_WIDTH-1:0] addr; // Address + rand bit [`DATA_WIDTH-1:0] data; // Data + rand bit [`DATA_WIDTH_DIV_8-1:0] strb; // Byte enable + rand bit [2:0] size; // Size + rand bit [7:0] length; // Length `uvm_object_utils_begin(axi_transaction) `uvm_field_enum(axi_transaction_type_t, txn_type, UVM_DEFAULT) `uvm_field_int(addr, UVM_DEFAULT) `uvm_field_int(data, UVM_DEFAULT) `uvm_field_int(strb, UVM_DEFAULT) + `uvm_field_int(size, UVM_DEFAULT) + `uvm_field_int(length, UVM_DEFAULT) `uvm_object_utils_end // Constructor @@ -18,6 +22,34 @@ class axi_transaction extends uvm_sequence_item; super.new(name); endfunction + // Get transaction id - made up of sequence ID and trasacton ID + function axi_transaction_id_t get_tid(); + axi_transaction_id_t tid; + + tid.seq_id = this.get_sequence_id(); + tid.txn_id = this.get_transaction_id(); + + return tid; + endfunction + + // Show transaction id - sequence ID:trasacton ID + function string show_tid(); + return $sformatf("%0h:%0h", this.get_sequence_id(), this.get_transaction_id()); + endfunction + + // Show transaction tag - Transaction type:sequence ID:trasacton ID + function string show_tag(); + return $sformatf("%s:%0h:%0h", this.txn_type.name(), this.get_sequence_id(), this.get_transaction_id()); + endfunction + + // Wait for transaction to be done + task wait_for_done(); + uvm_event finished_event; + + finished_event = get_event_pool().get("finished"); + finished_event.wait_on(); + endtask + // Copy method for cloning virtual function uvm_object clone(); axi_transaction copy; @@ -26,6 +58,8 @@ class axi_transaction extends uvm_sequence_item; copy.addr = this.addr; copy.data = this.data; copy.strb = this.strb; + copy.size = this.size; + copy.length = this.length; return copy; endfunction @@ -36,7 +70,9 @@ class axi_transaction extends uvm_sequence_item; return (this.txn_type == other.txn_type) && (this.addr == other.addr) && (this.data == other.data) && - (this.strb == other.strb); + (this.strb == other.strb) && + (this.size == other.size) && + (this.length == other.length); endfunction endclass : axi_transaction diff --git a/src/axi/axi_types.sv b/src/axi/axi_types.sv index d6237dd..efe71cd 100644 --- a/src/axi/axi_types.sv +++ b/src/axi/axi_types.sv @@ -1,13 +1,18 @@ // AXI Types package axi_types; + typedef struct { + int seq_id; + int txn_id; + } axi_transaction_id_t; + // Agent type - typedef enum { - MANAGER, - SUBORDINATE + typedef enum bit { + MANAGER = 1'b0, + SUBORDINATE = 1'b1 } axi_agent_type_t; - typedef enum { - AXI_READ, - AXI_WRITE + typedef enum bit { + AXI_READ = 1'b0, + AXI_WRITE = 1'b1 } axi_transaction_type_t; endpackage diff --git a/tb/tb_seq_base.sv b/tb/tb_seq_base.sv index bcda858..6abfc16 100644 --- a/tb/tb_seq_base.sv +++ b/tb/tb_seq_base.sv @@ -36,6 +36,7 @@ class axi_simple_seq extends axi_m_seq_base; // Task to start the sequence virtual task body(); + axi_transaction txns[$]; axi_transaction txn; super.body(); @@ -48,6 +49,16 @@ class axi_simple_seq extends axi_m_seq_base; data != {`DATA_WIDTH{1'b0}}; strb == 'hf; // Example byte enable }); + `uvm_info("axi_simple_seq", $sformatf("Starting %s transaction [%0s]", + txn.txn_type.name(), txn.show_tag()), UVM_LOW) + txns.push_back(txn); + end + + `uvm_info("axi_simple_seq", $sformatf("Waiting for %0d txns", txns.size()), UVM_LOW) + foreach (txns[i]) begin + `uvm_info("axi_simple_seq", $sformatf("Waiting for txn %s", txns[i].show_tag()), UVM_LOW) + txns[i].wait_for_done(); + `uvm_info("axi_simple_seq", $sformatf("Done waiting for txn %s", txns[i].show_tag()), UVM_LOW) end endtask endclass : axi_simple_seq diff --git a/tb/test_base.sv b/tb/test_base.sv index 3865132..eb100cb 100644 --- a/tb/test_base.sv +++ b/tb/test_base.sv @@ -12,8 +12,12 @@ class test_base extends uvm_test; // ------------------------------------------------------------ function void build_phase(uvm_phase phase); + uvm_root ur; super.build_phase(phase); + ur = uvm_root::get(); + ur.set_timeout(10us); + env = tb_env::type_id::create("env", this); tb_printer = new("tb_printer");