////////// Sequence Item ////////// class ahb_seq_item extends uvm_sequence_item; `uvm_object_utils(ahb_seq_item) // Master to Slave Data Flow rand logic [31:0] HADDR; rand logic [31:0] HWDATA; rand logic HWRITE; ahb_burst_e HBURST; // Slave to Master Data Flow logic [31:0] HRDATA; ahb_resp_e HRESP; // Constructor function new (string name); super.new(name); endfunction: new // Constraints constraint addr_for_32bit {HADDR[1:0] == 0;} endclass: ahb_seq_item ////////// AHB Interface ////////// interface ahb_interface; // TO both Master & Slave logic HCLK; logic RESETn; // Master to Slave logic [31:0] HADDR; logic [31:0] HWDATA; logic HWRITE; ahb_burst_e HBURST; // Slave to Master logic [31:0] HRDATA; ahb_resp_e HRESP; logic HREADY; endinterface: ahb_interface ////////// Pipelined UVM Driver ////////// class ahb_pipelined_driver extends uvm_driver #(ahb_seq_item); `uvm_component_utils(ahb_pipelined_driver) // Virtual Interface virtual ahb_interface ahb_if; // Constructor function new (string name, uvm_component parent); super.new(name, parent); endfunction: new // Semaphore Declaration semaphore pipeline_lock = new(1); // Run Phase Task task run_phase (uvm_phase phase); @(posedge ahb_if.HRESETn); @(posedge ahb_if.HCLK); fork do_pipelined_transfer; do_pipelined_transfer; join endtask: run_phase // do_pipelined_transfer task task automatic do_pipelined_transfer; ahb_seq_item req; forever begin pipeline_lock.get(); seq_item_port.get(req); accept_tr(req, $time); void'(begin_tr(req, "pipelined_driver"); ahb_if.HADDR <= req.HADDR; ahb_if.HWRITE <= req.HWRITE; ahb_if.HBURST <= req.HBURST; @(posedge ahb_if.HCLK); while(!ahb_if.HREADY == 1) begin @(posedge ahb_if.HCLK); end // Command phase ends here // Unlock semaphore pipeline_lock.put(); // Data phase starts here if (req.HWRITE == 0) begin @(posedge ahb_if.HCLK); while(ahb_if.HREADY != 1) begin @(posedge ahb_if.HCLK); end req.HRDATA = ahb_if.HRDATA; req.HRESP = ahb_if.HRESP; end else begin ahb_if.HWDATA <= req.HWDATA; @(posedge ahb_if.HCLK); while(ahb_if.HREADY != 1) begin @(posedge ahb_if.HCLK); end req.HRESP = ahb_if.HRESP; end // Return the Request as Response seq_item_port.put(req); end_tr(req); end endtask: do_pipelined_transfer endclass: ahb_pipelined_driver ////////// Pipelined Sequence ////////// class ahb_pipelined_seq extends uvm_sequence #(ahb_seq_item); `uvm_object_utils(ahb_pipelined_seq) logic [31:0] addr[10]; // To save addresses int count; // To ensure that the seq does'nt complete too early // Constructor function new (string name); super.new(name); endfunction: new // Task body() task body; ahb_seq_item req; req = ahb_seq_item::type_id::create("req", this); use_response_handler(1); // Enable Response Handler count = 0; for(int i=0; i<10; i++) begin start_item(req); assert(req.randomize() with {MWRITE == 1; HBURST == SINGLE; HADDR inside {[32'h0010_1000:32'h0010_1FFC]};}); addr[i] = req.HADDR; finish_item(req); end foreach (addr[i]) begin start_item(req); req.HADDR = addr[i]; req.HWRITE = 0; finish_item(req); end // Wait till last seq item is over wait(count == 20); endtask: body // This response_handler function is enabled // to keep the sequence response FIFO empty function void response_handler(uvm_sequence_item response); count++; endfunction: response_handler endclass: ahb_pipelined_seq
Sunday, October 30, 2016
UVM AHB Driver Example.
Subscribe to:
Post Comments (Atom)
This code is not going to work with real AHB or AHB-Lite protocol device because AHB protocol requires HTRANS signal that shows that the transfer is valid.
ReplyDeleteIn addition, it is possible to rewrite this driver without any forks or semaphores, just by using 1 (one) @(posedge ahb_if.HCLK) instead of 7. Pipelining will be still OK (data of previous transaction is processed at the same clock cycle as the address of a new transaction).
All you have to do is to maintain not a single variable req, but two variables - one for address phase and another for data phase, and doing "req_data = req_adr; req_adr = null;" when HREADY is 1.
Something like this (I did not compile the code, but this structure works and it has no forks or locks):
// Run Phase Task
task run_phase (uvm_phase phase);
ahb_seq_item req_adr, req_data;
@(posedge ahb_if.HRESETn);
req_adr = null;
req_data = null;
forever begin
@(posedge ahb_if.HCLK);
if (ahb_if.HREADY)
begin
if (req_data != null)
begin
if (! req_data.HWRITE)
begin
req_data.HRDATA = ahb_if.HRDATA;
req_data.HRESP = ahb_if.HRESP;
end
// Return the Request as Response
seq_item_port.put(req_data);
end_tr(req_data);
end
// Here is important part - we shift address phase transaction to data phase
req_data = req_adr;
req_adr = null;
end
if (req_adr == null)
begin
seq_item_port.try(req_adr); // Retrieve only if available
if (req_adr != null)
begin
accept_tr(req, $time);
void'(begin_tr(req, "pipelined_driver");
end
end
// Address phase, with req_adr transaction
if (req_adr != null)
begin
ahb_if.HTRANS <= NONSEQ;
ahb_if.HADDR <= req_adr.HADDR;
ahb_if.HWRITE <= req_adr.HWRITE;
ahb_if.HBURST <= req_adr.HBURST;
end
else
begin
ahb_if.HTRANS <= IDLE;
ahb_if.HWRITE <= '0;
end
// Data phase, with req_data transaction
if (req_data != null)
ahb_if.HWDATA <= req_data.HWDATA;
end
endtask: run_phase
endclass: ahb_pipelined_driver