Jump to content

Recommended Posts

Hi all,


I have a SPI interface, it has three modes to transfer the data out on IO[0:7]. In SPI standard there is just IO[0] is used. QPI mode, it uses IO[3:0] and In OPI mode, the whole IO are used. When driver receives a req from sequencer, it drives that req to the interface depending on modes. The interface sometimes goes with SPI, sometimes QPI or OPI. And I want to monitor the tx data, but I dont't know how to setup the monitor to sample the data corresponding to the mode that the driver has used. Are there ways to make monitor works in the same mode with the driver?


Thank you!


Share this post


Link to post
Share on other sites

Hello,

 

In general in any uvm_agent, driver and monitor are bind with interface, an interface which declares signal, clocking block, function, task many more..

for SPI bus, data transfer can be done in any mode as you said SPI,DPI(Dual), QPI, OPI(Octa) also there is flexibility of cmd-addr-data can be send different fashion as per memory device support.

 

for verification purpose what i can recommend you is : when spi controller running in SPI mode which mean only 2 pins are supposed to toggle(im ignoring cs and clk for this discussion), then driver can drive other 6 pins to 'High-Z' so that monitor can identify running mode. generally signal defined in interface are of type "LOGIC", it have 4 values(0,1,X,Z) in which monitor logic can work on signal value. Similarlly for QPI, drive remaining 4 pins to 'High-Z'.

 

I hope that help!!

 

With Best Regards

Ruchir

Share this post


Link to post
Share on other sites

I'd rather suggest to have a configuration field that states in which mode you are (single, dual, quad, octa). This field could be part of a configuration object that is shared by both objects:

typedef enum { SINGLE, DUAL, QUAD, OCTA } mode_t;

class spi_config extends uvm_object;
  rand mode_t mode;
  // ...
endclass


class spi_driver extends uvm_driver #(...);
  spi_config cfg;
  // ...
endclass


class spi_monitor extends uvm_monitor #(...);
  spi_config cfg;
  // ...
endclass


class spi_agent extends uvm_agent;
  spi_config cfg;
  spi_driver driver;
  spi_monior monitor;

  virtual function void build_phase(...);
    // ...

    // create and randomize config
    // ...
    
    driver.cfg = cfg;
    monitor.cfg = cfg;
  endfunction

Share this post


Link to post
Share on other sites

 

for verification purpose what i can recommend you is : when spi controller running in SPI mode which mean only 2 pins are supposed to toggle(im ignoring cs and clk for this discussion), then driver can drive other 6 pins to 'High-Z' so that monitor can identify running mode. generally signal defined in interface are of type "LOGIC", it have 4 values(0,1,X,Z) in which monitor logic can work on signal value. Similarlly for QPI, drive remaining 4 pins to 'High-Z'.

 

What if you have to verify an SPI master using the same UVC? In that case you can't rely that the design is driving Z when in a certain mode. This is exactly what you need to verify. I'd avoid making such assumptions.

Share this post


Link to post
Share on other sites

What if you have to verify an SPI master using the same UVC? In that case you can't rely that the design is driving Z when in a certain mode. This is exactly what you need to verify. I'd avoid making such assumptions.

 

Hello Tudor,

 

Thanks for sharing your thought,

 

But what i understood from initial question is development of monitor when UVC as master. ( i might be wrong)

 

still if UVC as Master( which i assume SPI Controller) then it can control the respective signal and give flexibility to implementer to drive 'Z' on ports and monitor can capture 'Z', but if UVC as slave( Which i assume SPI Memory device) then i think it have to sample all/selected signal as per memory device behavior. Which means read and decode commands/Addrress/data of SPI protocol and sample/drive line accordingly. Since SLAVE dont know what kind of command will controller initiate.

 

Regards

Ruchir

Share this post


Link to post
Share on other sites

Thanks for your replies,

I helped me.

 

By the way, let me ask one more question.

The clock in my testbench can run with different frequency, in my spi_top_tb I instantiate a DUT hook up to the interface and then define the clock generation like the below

 
module spi_top_tb;
 
parameter        cycle_1 = 3.5;
 
SPI dut(svif);
 
 //Clock generation
        always 
                #cycle_1 svif.SCK_PAD = ~svif.SCK_PAD;
...
 
How can I edit it to make clock cycle become configurable?
 

 

Thank you and Best regards,

Nhat 

Share this post


Link to post
Share on other sites

I would prefer an object oriented approach, i.e. a base class to implement common functionality and derived classes for specialization. Then, depending on the uvm_test randomization, type substitution by using the UVM factory. This approach reduces the code changes required in some scenarios.

Regards

Share this post


Link to post
Share on other sites

Hello Nhat,

 

An Ideal testbench should be fully controllable, modular and interoperable, which includes test should control generation of kind of stimulus, clock frequency, reset control, test signal. 

So to attain this i would recommend to have agent for each kind of signal ie

1) bus agents( data exchange bus like AXI, AMBA): normally drives/monitor bus signal and controlled by sequence.

2) Clock Agent : which generates all system clocks and clock frequency can be controlled by test/virtual sequence. here agent config is useful to control agent/clock for example :

// Virtual Interface
virtual clock_interface CLOCK;
//------------------------------------------
// Data Members
//------------------------------------------
// Is the agent active or passive
uvm_active_passive_enum active = UVM_ACTIVE;
// default clock Period in Mhz
int unsigned clock_period = 30;
// default duty cycle in terms of %age
int unsigned duty_cycle = 50;
// default start latencies in NS
int unsigned start_latency = 100;
// default clock state
bit default_state = 0;
// default jitter percentage
int unsigned jitter = 0;
// is clock stall during reset
bit is_clk_stall_at_reset = 0;
// ID flag
int id=0;

so when multiple clock_agent[x] is instantiated in uvm_env then id is used to configure agent as clock_agent[x].clock_period = XXX Mhz. so if uvm_env didnt program clock_period then default value will be consider. Also test/virtual sequence can override clock_period value as per requirement.

 

3) reset agent : similarly reset agent will control reset generation as a default value from agent config or controlled/overridden  value from test/virtual sequence.

 

I think this approach might you!!

 

Regards

Ruchir

Share this post


Link to post
Share on other sites

I think using the configuration object to share a variable, or using the factory are both good solutions.  I definitely also recommend against using the state of bus signals to figure out the type of protocol it should expecting; it should already know this information.

 

I've implemented an agent for DUT reset(s).  If your clock generation does not change from test to test, I think an agent is not required;  simply have an (optional) driver in the environment that drives the virtual interface; you can extend the driver to change clock characteristics.

Share this post


Link to post
Share on other sites

well brian, i agree with you if clock doesn't change much from test to test then there is no such requirement agent. Here drawback is on any change in testbench user need to recompile for every test.

But the idea of having clock agent is to have complete solution for control-ability and observability. Also its a one time effort to create agent and this agent can be plug-in to any UVM verification environment. secondly having a monitor in clock_agent will helps in getting clock frequency which further can be used for coverage sample( basically help in capturing functional coverage where verification objective is to check whether verification have test the design with different clock ranges). 

 

in the end its a trade off between QUALITY vs COST vs TIME. user need to balance as verification is never ending process.

Share this post


Link to post
Share on other sites

Thanks all, alot of useful information!

Ruchir, the approach about making agents brings me new idea that haven't thought about it, it's helpful information to me. About the reset agent, Do we need to write a scoreboard to check all the signal states whether or not it is reseted? Are there other approaches to check the reset status of the DUT?

In my DUT to reset it, I need to send out an opcode instead of controling a reset signal. I made a agent called spi_master to handle sending out all of opcodes, if I make a new reset agent, it seems that I need to reuse the driver from the spi_master agent, i feel something exessive! Could you give me some advice?

For enable signal agnets, do we need to write a seperate scoreboard to check for each case?

Thanks

Nhat

Share this post


Link to post
Share on other sites

I'd rather suggest to have a configuration field that states in which mode you are (single, dual, quad, octa). This field could be part of a configuration object that is shared by both objects:

typedef enum { SINGLE, DUAL, QUAD, OCTA } mode_t;

class spi_config extends uvm_object;
  rand mode_t mode;
  // ...
endclass


class spi_driver extends uvm_driver #(...);
  spi_config cfg;
  // ...
endclass


class spi_monitor extends uvm_monitor #(...);
  spi_config cfg;
  // ...
endclass


class spi_agent extends uvm_agent;
  spi_config cfg;
  spi_driver driver;
  spi_monior monitor;

  virtual function void build_phase(...);
    // ...

    // create and randomize config
    // ...
    
    driver.cfg = cfg;
    monitor.cfg = cfg;
  endfunction

Hi Tudor,

I have a idea like this, I want to put the mode_t as rand transferred item for sequence, when driver gets this, it will configure for all other components like monitors and slave driver into the same mode as it. Is this possible? If not so, could you give me the relevant way to do that?

Thanks

Nhat

Share this post


Link to post
Share on other sites

I'm guessing your device can only be in one mode at a time and this is configured via some fields in some configuration registers. It doesn't make sense to start sending OCTA frames when the device is configured for DUAL mode operation, hence there's no point in having mode a field in your sequence/item. When you configure your device for a certain mode (via some register/bus sequence) you need to reach into the configuration for your SPI agent and update that with the appropriate value (e.g. write registers to configure the device for QUAD, set the config field in the SPI agent to QUAD as well).

Share this post


Link to post
Share on other sites

I'm guessing your device can only be in one mode at a time and this is configured via some fields in some configuration registers. It doesn't make sense to start sending OCTA frames when the device is configured for DUAL mode operation, hence there's no point in having mode a field in your sequence/item. When you configure your device for a certain mode (via some register/bus sequence) you need to reach into the configuration for your SPI agent and update that with the appropriate value (e.g. write registers to configure the device for QUAD, set the config field in the SPI agent to QUAD as well).

Hi Tudor,

Your guessing is correct, by default the device work with SPI. To switch modes, spi master needs to send out the opcode to update the registers to configure the device into a certain mode. It turns out that if I want to test the device in OCTA mode, my first seq is to configure the device with OCTA mode by default standard, after that the device turns to work with the updated mode. The problem is that how can we let the SPI agent switch its mode during run time simulation? Can you give me some advice?

Thanks

Nhat

Share this post


Link to post
Share on other sites

When you start your config sequence, that's when you can also update you agent's config.

 

Hi Tudor

 

Thanks for your reply

 

The below is my code.

I think with this code I can only configure mode before the run phase and I don't know how to change the spi_config when the simulation gets into the run phase. How should I change?

virtual class spi_base_sequence extends uvm_sequence #(spi_transaction);
                  
   `uvm_declare_p_sequencer(spi_sequencer)
                  
   function new(string name="spi_base_seq");
      super.new(name);
   endfunction    
                  
   virtual task pre_body();
      if (starting_phase!=null) begin
         `uvm_info(get_type_name(),
                   $sformatf("%s pre_body() raising %s objection", 
                             get_sequence_path(),
                             starting_phase.get_name()), UVM_MEDIUM);
         starting_phase.raise_objection(this);
      end         
   endtask        
                  
   virtual task post_body();
      if (starting_phase!=null) begin
         `uvm_info(get_type_name(),
                   $sformatf("%s post_body() dropping %s objection", 
                             get_sequence_path(),
                             starting_phase.get_name()), UVM_MEDIUM);
         starting_phase.drop_objection(this);
      end         
   endtask        
                  
endclass : spi_base_sequence

class spi_seq_req extends spi_base_sequence ;                                                                                                                                                                         
function new(string name = "spi_seq_req"); 
super.new(name);   
endfunction : new  
                   
spi_transaction req; 
                   
`uvm_object_utils(spi_seq_req) 
                   
virtual task body(); 
                   
`uvm_do_with(req, {opcode==8'h03;})    // This seq is transferred by SPI standard
`uvm_do_with(req, {opcode==8'h06;})    // Write Enable opcode, transferred by SPI
`uvm_do_with(req, {opcode==8'hE8;})    // Select OPI MODE, transferred by SPI
`uvm_do_with(req, {opcode==8'h04;})    // After E8 opcode, the subsequence sequences will work with OPI
`uvm_do_with(req, {opcode==8'h02;})    // Page Program, transferred by OPI
                   
endtask : body     
                   
endclass: spi_seq_req

In spi_test.sv:

spi_config cfg =new ;

function void build_phase(uvm_phase phase);
//Set configure mode for Driver and Monitor
mode_t = STR;
std_t =SPI;
cfg.std_t = std_t;
cfg.mode = mode_t;

spi_env = spi_env::type_id::create(.name("spi_env"), .parent(this)); 
uvm_config_db#(uvm_object_wrapper)::set(this,"*.spi_seqr.main_phase", "default_sequence",spi_seq_req::type_id::get());

 

 

Share this post


Link to post
Share on other sites

well brian, i agree with you if clock doesn't change much from test to test then there is no such requirement agent. Here drawback is on any change in testbench user need to recompile for every test.

But the idea of having clock agent is to have complete solution for control-ability and observability. Also its a one time effort to create agent and this agent can be plug-in to any UVM verification environment. secondly having a monitor in clock_agent will helps in getting clock frequency which further can be used for coverage sample( basically help in capturing functional coverage where verification objective is to check whether verification have test the design with different clock ranges). 

 

in the end its a trade off between QUALITY vs COST vs TIME. user need to balance as verification is never ending process.

Thanks all, alot of useful information!

Ruchir, the approach about making agents brings me new idea that haven't thought about it, it's helpful information to me. About the reset agent, Do we need to write a scoreboard to check all the signal states whether or not it is reseted? Are there other approaches to check the reset status of the DUT? 

In my DUT to reset it, I need to send out an opcode instead of controling a reset signal. I made a agent called spi_master to handle sending out all of opcodes, if I make a new reset agent, it seems that I need to reuse the driver from the spi_master agent, i feel something exessive! Could you give me some advice?

For enable signal agnets, do we need to write a seperate scoreboard to check for each case? 

Thanks

Nhat 

Share this post


Link to post
Share on other sites

 

The below is my code.

I think with this code I can only configure mode before the run phase and I don't know how to change the spi_config when the simulation gets into the run phase. How should I change?

 

 

You can change the configuration during the run phase; in your sequence, at the exact moment you want to change the mode of your agent monitor, just grab the configuration object for your agent and update the mode directly.

 

Use the sequencer to get access to the agent config object.  I like using the database:

 

    if( !uvm_config_db #( spi_config )::get( m_sequencer, "", "AGENT_CONFIG", cfg ))

      `uvm_fatal(report_id, "cannot find spi config resource" )

Share this post


Link to post
Share on other sites

Thanks all, alot of useful information!

Ruchir, the approach about making agents brings me new idea that haven't thought about it, it's helpful information to me. About the reset agent, Do we need to write a scoreboard to check all the signal states whether or not it is reseted? Are there other approaches to check the reset status of the DUT? 

In my DUT to reset it, I need to send out an opcode instead of controling a reset signal. I made a agent called spi_master to handle sending out all of opcodes, if I make a new reset agent, it seems that I need to reuse the driver from the spi_master agent, i feel something exessive! Could you give me some advice?

For enable signal agnets, do we need to write a seperate scoreboard to check for each case? 

Thanks

Nhat 

 

 

Reset @ DUT : during hard reset DUT or any digital design will restore to its initial predefined condition. which means all internal register, fifo, state machines are reset to its predefine or known state, or in short to its reset state. In addition to this, all output port are also reset`d, which means verification engg also need to check the reset value of output port using assertions( which i normally do or suggest ) apart from register values.

 

Reset model : to apply reset you can create a interface which contain all reset signals (as some design have more than 1 reset signal+ test reset signals). This reset interface handle passed to your uvm_env by config_db. And finally in your uvm_env you can create task api which controls the reset with predefined intial state. Now test can call this reset API to drive reset condition as per requirement with time as parameter.  

CAUTION : If you want to apply reset multiple times during same simulation cycle, then you must take care of sequence should not block any sequencer. And reset information should be pass to all agent.

 

so to answer your query regarding new reset agent, i would suggest a task would gives more flexibility and assertions will help in checking default state of DUT signals(so no need to create separate scoreboard). Im also assuming in your case you are validating spi slave device which might not have reset port, so initially you can just check the default state on spi line like weak pull up/down or 'Z', then start driving signal with different opcodes.

 

I Hope that helps!!

If i still not clear, please let me know.

 

 

Regards

/Ruchir

Share this post


Link to post
Share on other sites

Reset @ DUT : during hard reset DUT or any digital design will restore to its initial predefined condition. which means all internal register, fifo, state machines are reset to its predefine or known state, or in short to its reset state. In addition to this, all output port are also reset`d, which means verification engg also need to check the reset value of output port using assertions( which i normally do or suggest ) apart from register values.

 

Reset model : to apply reset you can create a interface which contain all reset signals (as some design have more than 1 reset signal+ test reset signals). This reset interface handle passed to your uvm_env by config_db. And finally in your uvm_env you can create task api which controls the reset with predefined intial state. Now test can call this reset API to drive reset condition as per requirement with time as parameter.  

CAUTION : If you want to apply reset multiple times during same simulation cycle, then you must take care of sequence should not block any sequencer. And reset information should be pass to all agent.

 

so to answer your query regarding new reset agent, i would suggest a task would gives more flexibility and assertions will help in checking default state of DUT signals(so no need to create separate scoreboard). Im also assuming in your case you are validating spi slave device which might not have reset port, so initially you can just check the default state on spi line like weak pull up/down or 'Z', then start driving signal with different opcodes.

 

I Hope that helps!!

If i still not clear, please let me know.

 

 

Regards

/Ruchir

 

Hi Ruchir

 

Thanks for yor help a lot. Thanks to your suggestion, I have found this useful example for reset agent

http://forums.accellera.org/files/file/111-cadence-reset-example-and-package/

 

Let me ask one more question. I​'m verifying for a read opcode of a SPI memory device DUT. The memory address boundary is from 0 to 0x3FFFFF. The memory is modeled as a file that contains the data pattern for the memory. The address is split into Page address and Byte address, each page has 256 bytes. To read the data, I need to send out the command: one byte opcode + four byte of Address + dummy bytes. The DUT then gets into a continuous read mode, it means it keeps shifting the data output as long as the Chip select signal is still asserted. From this we can see that, the number of data output is however we want. This makes me think a lot how to cover for this. How should I define the coverage point for the number of data output?  Besides, Should I verify for every address of the memory and what kind of data pattern I need to cover? Could you and anyone else help me?

 

Thanks and best regards,

Nhat ​​​

 

 

 

 

Share this post


Link to post
Share on other sites

Hey Nhat,

 

Thanks for sending reset agent link, I haven't check it all but looks good.

 

Regarding Coverage on SPI protocol, to my best understanding there are 9 parameters through which we can complete SPI interface coverage :

1) Operation direction -  { IN, OUT} // Direction wrt to memory device.

2) Operation on      -  { MEMORY, REGISTER} // memory or device register operation

3) PAD               -  { SINGLE, DUAL, QUAD, OCTA} // pad in use during operation

4) operation flow    -  { OPC, ADDR, DUMMY, MODE, DATA} //

5) data size         -  { SMALL_PAGE, SINGLE_PAGE, MULTI_PAGE} // small_page == {1-255 bytes}, single_page == 256 bytes, multi page == 256 x n[2-10]

6) addr size         -  { 3BYTE, 4BYTE} //

7) addr pointer      -  { START, MIDDLE, END, RANDOM} // address pointer

8) addr alignment    -  { PAGE_ALIGNED, PAGE_UNALIGNED} // address is page aligned or not

9) edge type         -  { SDR, DDR} // Single edge data capture or Dual edge

 

From above enum, you can customize your stimulus generation and protocol coverage. I hope that would help you.

 

Regards

/Ruchir

Share this post


Link to post
Share on other sites

Hey Nhat,

 

Thanks for sending reset agent link, I haven't check it all but looks good.

 

Regarding Coverage on SPI protocol, to my best understanding there are 9 parameters through which we can complete SPI interface coverage :

1) Operation direction -  { IN, OUT} // Direction wrt to memory device.

2) Operation on      -  { MEMORY, REGISTER} // memory or device register operation

3) PAD               -  { SINGLE, DUAL, QUAD, OCTA} // pad in use during operation

4) operation flow    -  { OPC, ADDR, DUMMY, MODE, DATA} //

5) data size         -  { SMALL_PAGE, SINGLE_PAGE, MULTI_PAGE} // small_page == {1-255 bytes}, single_page == 256 bytes, multi page == 256 x n[2-10]

6) addr size         -  { 3BYTE, 4BYTE} //

7) addr pointer      -  { START, MIDDLE, END, RANDOM} // address pointer

8) addr alignment    -  { PAGE_ALIGNED, PAGE_UNALIGNED} // address is page aligned or not

9) edge type         -  { SDR, DDR} // Single edge data capture or Dual edge

 

From above enum, you can customize your stimulus generation and protocol coverage. I hope that would help you.

 

Regards

/Ruchir

 

 

Hi Ruchir,

Your answer is a good direction, based on this I know what I need to cover. This is helpful to me. I appreciate that. But one thing I still unclear is that regarding to the data size, as I see on the comment words multi page == 256 x n[2-10], if so the maximum data size of SPI protocol is 10 pages per one transfer. For my DUT, If I want to take larger data size, I need to start multiple read operation. Is that correct? 

 

 

Best regards,

 

Nhat

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×