// Base Virtual Sequence Class
class base_vseq extends uvm_sequence #(uvm_sequence_item);
`uvm_object_utils(base_vseq)
// Target Agent Sequencers
uvm_sequencer #(ahb_txn) SQR_AHB;
uvm_sequencer #(axi_txn) SQR_AXI;
// Constructor
function new (string name = "base_vseq");
super.new(name);
endfunction: new
endclass: vseq_base
// Virtual Sequence Class
class my_vseq extends base_vseq;
`uvm_object_utils(my_vseq)
// Constructor
function new (string name = "my_vseq");
super.new(name);
endfunction: new
// Sequence Body Task
task body();
ahb_seqeunce ahb_seq;
axi_sequence axi_seq;
ahb_seq = ahb_sequence::type_id::create("ahb_seq");
axi_seq = axi_sequence::type_id::create("axi_seq");
fork
abh_seq.start(SQR_AHB);
axi_seq.start(SQR_AXI);
join
endtask
endclass: my_vseq
// Base Test Class
class base_test extends uvm_test;
`uvm_component_utils(base_test);
// Environment Class Instantiation
top_env Env;
// Constructor
function new (string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction: new
// Build Phase
function void build_phase (uvm_phase phase);
Env = top_env::type_id::create("Env");
endfunction: build_phase
// Method to Connect Sequencer Handles in VSEQ
function void init_vseq (base_vseq vseq);
vseq.SQR_AHB = test.env.ahb_agent.SQR_AHB;
vseq.SQR_AXI = test.env.axi_agent.SQR_AXI;
endfunction: init_vseq
endclass: base_test
// Main Test
class test extends base_test;
`uvm_component_utils(test)
// Constructor
function new (string name = "test", uvm_component parent = null);
super.new(name, parent);
endfunction: new
// Run Phase
task run_phase (uvm_phase phase);
// Create the Virtual Sequence
my_vseq vseq = my_vseq::type_id::create("vseq");
phase.raise_objection(this);
// Virtual Sequence Initialization
init_vseq(vseq);
// Start the Virtual Sequence
vseq.start(null);
phase.drop_objection(this);
endtask: run_phase
endclass: test
Sunday, October 30, 2016
UVM Virtual Sequence Example.
UVM AHB Driver Example.
////////// 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
UVM APB Driver Example.
// APB Bus Interface
interface apb_interface;
logic clk;
logic rstn;
logic [31:0] Addr;
logic [31:0] Write_Data;
logic RNW;
logic Valid;
logic Ready;
logic [31:0] Read_Data;
logic Error;
endinterface: apb_interface
// APB Bus Sequence Item Class
class apb_seq_item extends uvm_sequence_item;
`uvm_object_utils(apb_seq_item)
// Request fields
rand logic [31:0] addr;
rand logic [31:0] write_data;
rand bit read_not_write;
rand int delay;
// Response fields
bit error;
logic [31:0] read_data;
endclass: apb_seq_item
// APB Bus Driver Class
class apb_bus_driver extends uvm_driver #(apb_seq_item);
`uvm_component_utils(apb_bus_driver)
apb_seq_item req;
virtual apb_interface apb_if;
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction: new
task run_phase (uvm_phase phase);
apb_if.Valid <= 0;
apb_if.RNW <= 0;
// Out of Reset
@(posedge apb_if.rstn);
// Pin Level Transfer
forever begin
seq_item_port.get_next_item(req);
repeat(req.delay) begin
@(posedge apb_if.clk);
end
apb_if.Valid <= 1;
apb_if.Addr <= req.addr;
apb_if.RNW <= req.read_not_write;
if (req.read_not_write == 0) begin
apb_if.Write_Data <= req.write_data;
end
while(apb_if.Ready != 1) begin
@(posedge apb_if.clk);
end
// At the end of pin level bus transaction
// Copy response data into the req related fields
if (req.read_not_write == 1) begin
req.read_data <= apb_if.Read_Data;
end
req.error <= apb_if.Error;
apb_if.Valid <= 0;
seq_item_port.item_done();
end
endtask: run_phase
endclass: apb_bus_driver
// APB Bus Sequence
class apb_bus_seq extends uvm_sequence #(apb_seq_item)
`uvm_object_utils(apb_bus_seq)
function new(string name);
super.new(name);
endfunction: new
// No. of iterations
rand int iti = 30
apb_seq_item req;
task body();
req = apb_seq_item::type_id::create("req", this);
repeat(iti) begin
start_item(req);
assert (!req.randomize()
with {addr inside {[32`h1000_0000:32`h1000_001C]};})
begin
`uvm_error("Body", "Randomization Error");
end
finish_item(req);
`uvm_info ("Response Fields",
$sformatf("Read Data = %h", req.read_data), UVM_LOW)
end
endtask: body
endclass: apb_bus_seq
UVM PCM Driver Example.
// Interface Declaration
interface adpcm_int;
logic clk;
logic frame;
logic [3:0] data;
endinterface: adpcm_int
// Transaction item class adpcm_txn
class adpcm_txn extends uvm_sequence_item;
`uvm_object_util(adpcm_txn)
rand logic[3:0] data;
rand int delay;
function new (string name);
super.new(name);
endfunction: new
constraint del_bet_pac {delay inside {[1:20]};}
endclass: adpcm_txn
// Driver class adpcm_drv
class adpcm_drv extends uvm_driver #(adpcm_txn);
`uvm_component_utils (adpcm_drv)
adpcm_txn req;
virtual adpcm_int adpcm_if;
function new(string name, uvm_components parent)
super.new(name, parent);
endfunction: new
task run_phase (uvm_phase phase);
// Default conditions
adpcm_if.frame <= 0;
adpcm_if.data <= 0;
forever begin
seq_item_port.get_next_item(req);
repeat(req.delay) begin
@(posedge adpcm_if.clk);
end
adpcm_if.frame <= 1;
for (int i = 0; i < 8; i++) begin
@(posedge adpcm_if.clk);
adpcm_if.data <= req.data[3:0];
req.data = req.data >> 4;
end
adpcm_if.frame <= 0;
seq_item_port.item_done();
end
endtask: run_phase
endclass: adpcm_drv
// Sequence Class adpcm_seq
class adpcm_seq extends uvm_sequence #(adpcm_txn);
`uvm_component_utils (adpcm_seq)
adpcm_txn req;
rand int no_of_reps = 10;
function new (string name);
super.new(name);
endfunction: new
task body;
req = adpcm_txn::type_id::create("req", this);
for (int i = 0; i < no_of_reps; i++) begin
start_item(req);
if (!req.randomize()) begin
`uvm_error("BODY", "req randomization failed")
end
finish_item(req);
`uvm_info("ADPCM_SEQ BODY",
$sformatf("Transmitted frame %0d", i), UVM_LOW)
end
endtask: body
endclass: adpcm_seq
Saturday, October 29, 2016
AMBA 101.
Advanced Microcontroller Bus Architecture by ARM
- ASB - advanced system bus
- APB - advanced peripheral bus (used for low power peripheral modules)
- AHB - advanced high performance bus
- Arbitration
- round robin priority scheme (default priority: master0)
- fair chance
- random access
- high priority
- burst transfer
- undefined length or 4, 8, 16 in length
- fixed address, incremental address, or wrap address for cache line
- split transactions
- multiple masters
- through a central arbiter
- through multiple slave level arbiters in multi-layer AHB-Lite system.
- masters could be DMA controller, a peripheral core master interface, etc.
- wider address bus, separate read or write data bus
- larger data paths (multiplexed, not tri-state)
- pipelined operation
- wait-state insertion
- data-valid signal HREADY
- AHB and APB is linked through a bridge that acts as the master to the peripheral bus slave devices.
AHB Signals
- General
- HCLK
- HRESET
- Arbiter / Master
- HBUSREQ (one from each master)
- HGRANT (one for each master)
- Master / Slave
- HRESP ([1:0] or [0] in AHB-Lite)
- 00 - OKAY
- 01 - ERROR
- 10 - RETRY
- 11 - SPLIT
- HREADY (used by slave to insert wait state)
- when it's low (from slave) all control signals shall hold their values.
- HTRANS[1:0] (set according to RD or WR)
- 00 - IDLE
- 01 - BUSY (master can use BUSY to insert wait state during bursting)
- 10 - NONSEQ
- First transfer of an AHB sequence is always NONSEQ
- 11 - SEQ
- All transfer following the first shall be SEQ in nature.
- HWRITE
- HADDR [31:0] (muxed from individual masters to slaves and address decoder)
- HRDATA / HWDATA
- Slave / Arbiter
- HMASTER [3:0] / HMASTLOCK - from arbiter
- HSPLIT# [15:0] - one from each slave
- Address Decoder / Slave
- HSEL# (one for each slave unit)
- More from Masters to Slave
- HSIZE[2:0]
- HBURST[2:0]
- 4, 8, 16 in size
- incremental
- wrapping
- 000 - single
- 001 - INCR
- 010 - WRAP4
- 011 - INCR4
- 100 - WRAP8
- 101 - INCR8
- 110 - WRAP16
- 111 - INCR16
- HPROT[3:0] - protection control
- HLOCK# - locked transfer, one from each master
- lowered during the last transfer of an AHB sequence
- Example Auxilary Signals for AHB Masters
- REQ_VLD - input to bus master, asserted when HBUSREQ is asserted
- WR - input to bus master, high when HWRITE is high
- RD - input to bus master, high when HWRITE is low
- IN_ADDR[31:0] - input to bus master, used to decide HADDR[31:0]
- IN_DATA[31:0]
- LEN#
- LAST
- OUT_DATA[31:0] - output from bus master
- REQ_WR_DATA - output from bus master
- It shall be high when (REQ_ADDR & WR) to ensure data shall be put on data bus one cycle after address is on.
- When REQ_WR_DATA ==1, IN_DATA will be put to data bus in next cycle.
- REQ_RD_DATA - output from bus master
- It shall be high when it's a read transfer and HREADY == 1.
- REQ_ADDR - output from bus master
- It shall be high when (HREADY & HGRANT) to ensure master can put address in next cycle.
- When REQ_ADDR == 1, IN_ADDR will be put to address bus in next cycle.
AHB Phases
- address/control
- phase data phase
AHB Protocols
- bus master sends HBUSREQ to arbiter and an address, decoded by central decoder to provide a dedicated HSEL to selected slave unit.
- Slaves should only sample address when both HSEL and HREADY are high.
- If the slave is under re-configuration or needs to terminate a transaction, it saves the Hmaster number, and sends RETRY or SPLIT to arbiter and arbiter remove the requesting master from list until the targeted slave de-assert Hsplit. If RETRY is sent, the transaction is dropped.
- When the slave is ready, it signals the arbiter with the saved master number and the arbiter grants the bus access to the master to restart the transaction.
- No master can have more than one pending split transaction.
- Otherwise, master gets Hgrant and lock the bus.
- During burst mode (size indicated by HBURST[2:0]), master can insert wait state by sending HTRANS == BUSY. The slave has to send immediate OKAY in next cycle as for the case of IDLE and ignore the transfer.
- First burst cycle is always NONSEQUENTIAL.
- HADDR must be aligned to the HBURST size, that is, A[1:0] == 2'b00 in word transfer and A[0] == 1'b0 in halfword transfer.
- If a master is granted the bus without requesting it, it has to drive the transfer type IDLE.
APB Signals
- PCLK
- PRESETn - active LOW
- PSEL# - APB select
- PEN or PENABLE - APB strobe
- PADDR[31:0] - Address bus
- PWRITE - Transfer direction
- PWDATA[31:0] - Write data bus (can be 8, 16, 32 bit)
- PRDATA[31:0] - Read data bus
- PREADY
APB States
- IDLE : (~PSEL & ~PEN)
- SETUP : ( PSEL & ~PEN)
- ACCESS : ( PSEL & PEN)
AHB/APB Bridge Approach
- Cross clock domain data transfer
- handshake signaling method (double stage synchronizer)
- asynchronous FIFO
ASB Signals
- AGNT# - Bus grant
- AREQ# - Bus request
- BA[31:0] - Address bus
- BCLK - Bus clock
- BD[31:0] - Data bus
- BERROR - Error response
- BLAST - Last response
- BLOK - Locked transfer
- BnRES - Reset
- BPROT[1:0] - Protection control
- BSIZE[1:0] - Transfer size
- BTRAN[1:0] - Transfer bype (driven when AGNT# is asserted)
- ADDRESS-ONLY
- NON-SEQUENTIAL
- SEQUENTIAL
- BWAIT - Wait response
- BWRITE - Transfer direction
- DSEL# - Slave select
UVM Coverage.
Basics
- Put all covergroups in a class or module
- Use local variables in the class or module
- Make covergroups sensitive to a variable or explicitly sample a variable
- Have a coverage test plan
- Use automatic bins for simple covergroup
- Code coverage is not functional coverage
- line coverage
- block / statement coverage
- branch coverage
- path coverage
- toggle coverage
- expression coverage
- FSM coverage
- transition coverage
<coverpoint_name> : coverpoint <expression> { bins <bin_name> = { <list of values > } }
class coverage extends uvm_agent;
`uvm_component_utils (coverage)
tlm_analysis_fifo #(mem_req) req_fifo;
mem_req req;
mem_op op;
logic [15:0] addr;
covergroup mem_ops;
coverpoint memop {
bins action[] = {read, write};
}
coverpoint addr {
bins zeros = {0};
bins others = {[1 : 16'hFFFE]};
bins ones = {16'hFFFF};
}
edges : cross op, addr;
endgroup
covergroup alu_cv;
all_ops : coverpoint op;
a_op: coverpoint A {bins Ais0 = {'h00};}
b_op: coverpoint B {bins Bis0 = {'h00};}
endgroup
function new (string name, uvm_component parent);
super.new(name, parent);
mem_ops = new();
alu_cv = new();
endfunction : new
task run();
mem_data cln;
mem_req req_tx;
forever begin : run_loop
req_fifo.get (req_tx);
op = req_tx.op;
addr = req_tx.addr;
mem_ops.sample();
end
endtask : run
endclass
Example: automatic bins
typedef enum {add, and, xor, mul, rst, nop} op_t;
covergroup opcov;
coverpoint op;
endgroup : opcov
task sample_req;
A = req.A;
B = req.B;
op = req.op;
opcov.sample();
endtask
covergroup alu_cv2;
coverpoint op;
coverpoint A;
coverpoint B;
option.auto_bin_max = 4; // default to 64 bins
endgroup
Example: Basic Bins
covergroup opcov;
coverpoint op;
A00FF : coverpoint A {
bins zeros = { 0 };
bins ones = { 8'hFF };
}
B00FF : coverpoint B {
bins zeros_ones = { 0, 8'hFF };
}
endgroup
Bins with Ranges, Bins with sequences
typedef enum {add, and, xor, mul, rst, nop} op_t;
covergroup opcov;
coverpoint op {
bins single_cyc = {[add : xor] , rst, nop};
bins multi_cyc = {mul};
}
endgroup : opcov
// Automatic bins with ranges
bins onepervalue [] = {< list of="" values="" >};
bins n_bins [n] = { < list of="" values="" >};
bins threebins [3] = {[1:2],[2:6]};
// same as
// bins threebins [3] = {1,2,2,3,4,5,6};
// Bins with sequences
// run multi-cycle after reset
// bins bin_name = ( < value1 > => < value2 >);
covergroup opcov;
coverpoint op {
bins single_cycle = {[add : xor], rst, nop};
bins multi_cycle = {mul};
bins mult_rst = (mul => rst);
bins rst_mult = (rst => mul);
}
endgroup
Bins with multiple value transitions in sequences
// Bins with multiple value transitions // bins bin_name = (< value list > => < value list >); // 1, 2 => [3:5], 7 // 1=>3, 2=>3, 1=>4, 2=>4, 1=>5, 2=>5, 1=>7, 2=>7 bins op_rst[] = ( [add : nop ] => rst ); bins rst_mult[] = ( rst => [add : nop]); // multi-cycle after single-cycle bins singl_mul[] = ( [add : xor ], nop => mul ); // single-cycle after multi-cycle bins mul_sngl[] = ( mul => [add : xor ], nop ); // Run all operations twice in a row // bins <name> = (<value list> [* n]); bins <name> = (<value list> [* n:m]); // Ex: Run 3-5 Multiplies in a row bins twoops[] = ([add:nop] [*2]); bins manymult = (mul [* 3:5]); // Nonconsecutive Repetition // <value list> [= n:m]; // nonconsecutive operator // <value list> [-> n:m]; // goto operator // • Nonconsecutive Operator (=) matches if n:m values occur // in a list of value regardless of the terminating value. // • Goto Operator (->) matches if n:m values occur in a list // of values and then a terminating value appears. bins rstmulrst = (rst => mul [= 2] => rst); // rstmulrst (match): rst => mul => xor => mul => and => rst bins rstmulrstim = (rst => mul [-> 2] => rst); // rstmulrst and rstmulrstim (match): rst => mul => xor => and => mul => rst
Cross Coverage : Use Cross and Binsof to capture combinations of values
covergroup zeros_ones_ops;
all_ops : coverpoint op {
ignore_bins null_ops = {rst, nop};
}
a_op: coverpoint A {
bins zeros = {'h00};
bins others = {['h01:'hFE]};
bins ones = {'hFF};
}
b_op: coverpoint B {
bins zeros = {'h00};
bins others = {['h01:'hFE]};
bins ones = {'hFF};
}
basic : cross a_op, b_op, all_ops;
with_a0bin : cross a_op, b_op, all_ops {
bins a0bin = binsof (a_op.zeros);
}
// bins <bin> = binsof(<somebins>) && binsof(<otherbins>);
// bins <bin> = binsof(<somebins>) || binsof(<otherbins>);
zeros_ones : cross a_op, b_op, all_ops {
bins AorBzero = binsof (a_op.zeros) || binsof (b_op.zeros);
bins AorBones = binsof (a_op.ones) || binsof (b_op.ones);
ignore_bins the_others =
binsof (a_op.others) && binsof (b_op.others);
}
// intersect qualifier
// bins <bin> = binsof(<somebins> intersect (<value_list>))
zeros_ones_2 : cross a_op, b_op, all_ops {
bins add_bin = binsof (all_ops) intersect {add};
ignore_bins x = ! binsof (all_ops) intersect {add};
}
endgroup
Friday, October 28, 2016
SystemVerilog Question.
- Automatic vs Static
For a static task, multiple invocations of the same task will reference the same local variables. For an automatic task, the local variables will be unique to each invocation of the task. The function should be automatic so that there will be multiple memory allocations for the index variable to show increasing values instead of a single value.
- packed vs unpacked
Unpacked array is an array with gap between variables.
- Join / Join_any / Join_none
Join (and all done) / Join_any (or any done) / Join_none (none is done, non-blocking)(use "wait fork;" or implement watch_dog timer for "join_none" for synchronization)
- Wire vs LogicLogic and wire are almost the same except wire can be driven by multiple sources. Logic can only driven by single source.
- VirtualMeans "abstract"
Subscribe to:
Posts (Atom)