////////// 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