专栏首页摸鱼范式【UVM COOKBOOK】Testbench Architecture【二】

【UVM COOKBOOK】Testbench Architecture【二】

不想错过我的推送,记得右上角-查看公众号-设为星标,摘下星星送给我

欢迎大家加入2022届数字IC交流群,QQ群号 1060380138

模块级Testbench

考虑构建一个用于验证SPI主机DUT的testbench作为模块级testbench的一个例子。在这种情况下,UVM环境有两个agent—APB agent在其APB从机端口上处理总线传输,以及SPI agent在其SPI端口上处理SPI协议传输。整个UVM验证环境的结构在框图中进行了说明。让我们穿过testbench的每一层,并描述它是如何从上到下组合在一起的。

Testbench模块

在SPI模块级testbench中使用了两个顶层testbench模块。hdl_top模块包含SPI Master DUT、APB和SPI BFM以及apb_if、spi_if和intr_if接口。SPI Master DUT连接apb_if、spi_if和intr_if, apb_if、spi_if和intr_if分别连接APB从机和SPI主机BFM。两个initial块也封装在hdl_top中。第一个initial块使用uvm_config_db::set将BFM接口的虚接口句柄放置到UVM配置空间中。第二个initial块为APB接口生成一个时钟和一个复位信号。

module hdl_top; 
 ìnclude "timescale.v" 
// PCLK and PRESETn 
// 
 logic PCLK; 
 logic PRESETn; 
// 
// Instantiate the pin interfaces: 
// 
 apb_if APB(PCLK, PRESETn); 
 spi_if SPI(); 
 intr_if INTR();
// 
// Instantiate the BFM interfaces:
// 
 apb_monitor_bfm APB_mon_bfm(
  .PCLK    (APB.PCLK),
  .PRESETn (APB.PRESETn),
  .PADDR   (APB.PADDR),
  .PRDATA  (APB.PRDATA),
  .PWDATA  (APB.PWDATA),
  .PSEL   (APB.PSEL),
  .PENABLE (APB.PENABLE),
  .PWRITE  (APB.PWRITE),
  .PREADY  (APB.PREADY)
 );
 apb_driver_bfm APB_drv_bfm(
  .PCLK    (APB.PCLK),
  .PRESETn (APB.PRESETn),
  .PADDR   (APB.PADDR),
  .PRDATA  (APB.PRDATA),
  .PWDATA  (APB.PWDATA),
  .PSEL   (APB.PSEL),
  .PENABLE (APB.PENABLE),
  .PWRITE  (APB.PWRITE),
  .PREADY  (APB.PREADY)
 );
 spi_monitor_bfm SPI_mon_bfm(
  .clk  (SPI.clk),
 .cs   (SPI.cs),
  .miso (SPI.miso),
  .mosi (SPI.mosi)
 );
 spi_driver_bfm SPI_drv_bfm(
  .clk  (SPI.clk),
  .cs   (SPI.cs),
  .miso (SPI.miso),
  .mosi (SPI.mosi)
 );
 intr_bfm INTR_bfm(
  .IRQ  (INTR.IRQ),
  .IREQ (INTR.IREQ)
 );
// DUT
 spi_top DUT(
 // APB Interface:
  .PCLK(PCLK),
  .PRESETN(PRESETn),
  .PSEL(APB.PSEL[0]),
  .PADDR(APB.PADDR[4:0]),
  .PWDATA(APB.PWDATA),
  .PRDATA(APB.PRDATA),
  .PENABLE(APB.PENABLE),
  .PREADY(APB.PREADY),
  .PSLVERR(),
  .PWRITE(APB.PWRITE),
 // Interrupt output
  .IRQ(INTR.IRQ),
 // SPI signals
  .ss_pad_o(SPI.cs),
  .sclk_pad_o(SPI.clk),
  .mosi_pad_o(SPI.mosi),
  .miso_pad_i(SPI.miso)
 );
// Initial block for virtual interface wrapping: 
 initial begin 
  import uvm_pkg::uvm_config_db; 
   uvm_config_db #(virtual apb_monitor_bfm)::set(null, "uvm_test_top", "APB_mon_bfm", APB_mon_bfm); 
  uvm_config_db #(virtual apb_driver_bfm) ::set(null, "uvm_test_top", "APB_drv_bfm", APB_drv_bfm); 
  uvm_config_db #(virtual spi_monitor_bfm)::set(null, "uvm_test_top", "SPI_mon_bfm", SPI_mon_bfm); 
  uvm_config_db #(virtual spi_driver_bfm) ::set(null, "uvm_test_top", "SPI_drv_bfm", SPI_drv_bfm); 
  uvm_config_db #(virtual intr_bfm)       ::set(null, "uvm_test_top", "INTR_bfm", INTR_bfm); 
 end 
// 
// Initial blocks for clock and reset generation: 
// 
 initial begin 
  PCLK = 0; 
    forever #10ns PCLK = ~PCLK; 
 end 
 initial begin 
  PRESETn = 0; 
  repeat(4) @(posedge PCLK); 
  PRESETn = 1; 
 end
endmodule: hdl_top

hvl_top模块只导入uvm_pkg和spi_test_lib_pkg,其中包含可以运行的test的定义。它还包含调用run_test()方法来构造和启动指定的test的initial块,从而实现UVM phasing。

module hvl_top; 
 ìnclude "timescale.v" 
 import uvm_pkg::*; 
 import spi_test_lib_pkg::*; 
// UVM initial block: 
 initial begin 
  run_test(); 
 end 
endmodule: hvl_top 

Test

UVM构建过程中的下一个phase是build phase。对于SPI模块级示例,这意味着在首先创建和准备环境使用的所有相关配置对象之后构建spi_env组件。配置和构建过程对于大多数test case来说是很通用的,所以设计一个可扩展以创建特定test的test基类通常是很好的做法。

在SPI示例中,spi_env的配置对象包含SPI和APB配置对象的句柄。这就允许使用env配置对象将所有所需的子配置对象传递给env,来作为spi_env build方法的一部分。这种嵌套配置的“俄罗斯套娃”方法可用于多层次结构级别。

在env配置块中将agent的配置对象赋值给它们的句柄之前,先实例化,并且使用uvm_config_db::get方法赋值其虚接口,然后再进行配置。虚接口赋值给在hdl_top中设置的virtual BFM接口句柄。APB agent可以根据不同的test case进行不同的配置,所以它的配置过程被专用于基类中的特定虚方法。这就使得派生的test类可以重载此方法并根据需要来配置APB agent。

// 
// Class Description: 
// 
// 
class spi_test_base extends uvm_test; 
// UVM Factory Registration Macro 
// 
`uvm_component_utils(spi_test_base) 
//------------------------------------------ 
// Data Members 
//------------------------------------------ 
//------------------------------------------ 
// Component Members 
//------------------------------------------ 
// The environment class 
 spi_env m_env; 
// Configuration objects
 spi_env_config   m_env_cfg;
 apb_agent_config m_apb_cfg;
 spi_agent_config m_spi_cfg; 
// Register map 
 spi_register_map spi_rm; 
//Interrupt Utility 
 intr_util INTR; 
//------------------------------------------ 
// Methods 
//------------------------------------------ 
 extern virtual function void configure_apb_agent(apb_agent_config cfg); 
// Standard UVM Methods: 
 extern function new(string name = "spi_test_base", uvm_component parent = null); 
 extern function void build_phase( uvm_phase phase );
endclass: spi_test_base 
function spi_test_base::new(string name = "spi_test_base", uvm_component parent = null); 
 super.new(name, parent); 
endfunction 
// Build the env, create the env configuration 
// including any sub configurations and assigning virtural interfaces 
function void spi_test_base::build_phase( uvm_phase phase ); 
 virtual intr_bfm temp_intr_bfm; 
// env configuration 
 m_env_cfg = spi_env_config::type_id::create("m_env_cfg"); 
// Register map - Keep reg_map a generic name for vertical reuse reasons
 spi_rm = new("reg_map", null); 
 m_env_cfg.spi_rm = spi_rm; 
 m_apb_cfg = apb_agent_config::type_id::create("m_apb_cfg"); 
 configure_apb_agent(m_apb_cfg); 
 if (!uvm_config_db #(virtual apb_monitor_bfm)::get(this, "", "APB_mon_bfm", m_apb_cfg.mon_bfm)) ùvm_fatal(...) 
 if (!uvm_config_db #(virtual apb_driver_bfm) ::get(this, "", "APB_drv_bfm", m_apb_cfg.drv_bfm)) ùvm_fatal(...)
  m_spi_cfg.has_functional_coverage = 0;
  m_env_cfg.m_spi_agent_cfg = m_spi_cfg;
// Insert the interrupt virtual interface into the env_config: 
 INTR = intr_util::type_id::create("INTR"); 
    if (!uvm_config_db #(virtual intr_bfm)::get(this, "", "INTR_bfm", temp_intr_bfm) ) `uvm_fatal(...) 
 INTR.set_bfm(temp_intr_bfm); 
 m_env_cfg.INTR = INTR; 
 uvm_config_db #( spi_env_config )::set( this ,"*", "spi_env_config", m_env_cfg); 
 m_env = spi_env::type_id::create("m_env", this); 
// Override for register adapter: 
 register_adapter_base::type_id::set_inst_override(apb_register_adapter::get_type(), 
"spi_bus.adapter");
endfunction: build_phase 
// 
// Convenience function to configure the apb agent 
// 
// This can be overloaded by extensions to this base class 
function void spi_test_base::configure_apb_agent(apb_agent_config cfg);
  cfg.active = UVM_ACTIVE; 
 cfg.has_functional_coverage = 0; 
 cfg.has_scoreboard = 0; 
// SPI is on select line 0 for address range 0-18h 
 cfg.no_select_lines = 1; 
 cfg.start_address[0] = 32'h0;
 cfg.range[0] = 32'h18; 
endfunction: configure_apb_agent

如果要创建一个特定的test case,就需要扩展spi_test_base类,test编写人员可以利用在父类中定义的配置和build过程。因此,test编写人员只需要添加一个run_phase方法。在下面的示例中(简单且需要更新),run_phase方法实例化sequence,并在env中的相应的sequencer上启动它们。所有的配置过程都是由build_phase方法中调用的super.build_phase()方法执行的。

// 
// Class Description: 
// 
// 
class spi_poll_test extends spi_test_base; 
// UVM Factory Registration Macro 
// 
 `uvm_component_utils(spi_poll_test) 
//------------------------------------------ 
// Methods 
//------------------------------------------ 
// Standard UVM Methods: 
 extern function new(string name = "spi_poll_test", uvm_component parent = null);
 extern function void build_phase(uvm_phase phase); 
 extern task run_phase(uvm_phase phase); 
endclass: spi_poll_test 
        
function spi_poll_test::new(string name = "spi_poll_test", uvm_component parent = null); 
 super.new(name, parent); 
endfunction 
// Build the env, create the env configuration 
// including any sub configurations and assigning virtural interfaces 
function void spi_poll_test::build_phase(uvm_phase phase); 
 super.build_phase(phase); 
endfunction: build_phase 
        
task spi_poll_test::run_phase(uvm_phase phase); 
 config_polling_test t_seq = config_polling_test::type_id::create("t_seq"); 
 set_seqs(t_seq); 
 phase.raise_objection(this, "Test Started");
 t_seq.start(null);
 #100; 
 phase.drop_objection(this, "Test Finished"); 
endtask: run_phase 

Environment

SPI UVM环境中的下一层是spi_env。这个类包含许多子组件,即SPI和APB agent、scoreboard和functional coverage collector。实例化哪个子组件由spi_env配置对象中的变量决定。

在本例中,spi_env配置对象还包含一个实用程序,该实用程序包含一个检测中断的方法,可以被序列使用。spi_env_config类的内容如下:

// 
// Class Description: 
// 
// 
class spi_env_config extends uvm_object; 
 const string s_my_config_id = "spi_env_config"; 
 const string s_no_config_id = "no config"; 
 const string s_my_config_type_error_id = "config type error"; 
// UVM Factory Registration Macro 
// 
 `uvm_object_utils(spi_env_config) 
// Interrupt Utility - used in the wait for interrupt task 
// 
 intr_util INTR; 
//------------------------------------------ 
// Data Members 
//------------------------------------------ 
// Whether env analysis components are used: 
 bit has_functional_coverage = 0; 
 bit has_spi_functional_coverage = 1; 
 bit has_reg_scoreboard = 0; 
 bit has_spi_scoreboard = 1; 
// Whether the various agents are used: 
 bit has_apb_agent = 1; 
 bit has_spi_agent = 1; 
// Configurations for the sub_components
 apb_agent_config m_apb_agent_cfg;
 spi_agent_config m_spi_agent_cfg; 
// SPI Register model 
 uvm_register_map spi_rm; 
//------------------------------------------ 
// Methods 
//------------------------------------------ 
 extern task wait_for_interrupt; 
 extern function bit is_interrupt_cleared; 
// Standard UVM Methods: 
 extern function new(string name = "spi_env_config"); 
endclass: spi_env_config 
        
function spi_env_config::new(string name = "spi_env_config"); 
 super.new(name); 
endfunction 
// This task is a convenience method for sequences waiting for the interrupt
// signal 
task spi_env_config::wait_for_interrupt;
  INTR.wait_for_interrupt();
endtask: wait_for_interrupt
// Check that interrupt has cleared: 
function bit spi_env_config::is_interrupt_cleared; 
 return INTR.is_interrupt_cleared(); 
endfunction: is_interrupt_cleared 

在本例中,每个子组件都有构建配置的控制位。这为env的重用提供了终极灵活性。

在spi_env的build phase,使用uvm_config_db get()从配置空间检索spi_env_config的句柄。然后,build过程检查配置对象中的各种has_字段,以确定是否构建子组件。在APB和SPI agent的情况下,还有一个额外的步骤,即从env配置对象中解包每个agent的配置对象,然后在任何本地修改之后在env配置表中设置agent配置对象。

在connect phase阶段,再次使用spi_env配置对象来确定要建立哪个TLM连接。

// 
// Class Description: 
// 
// 
class spi_env extends uvm_env; 
// UVM Factory Registration Macro 
// 
 `uvm_component_utils(spi_env) 
//------------------------------------------ 
// Data Members 
//------------------------------------------ 
 apb_agent m_apb_agent; 
 spi_agent m_spi_agent; 
 spi_env_config m_cfg; 
 spi_scoreboard m_scoreboard; 
// Register layer adapter
 reg2apb_adapter m_reg2apb;
// Register predictor 
 uvm_reg_predictor#(apb_seq_item) m_apb2reg_predictor; 
//------------------------------------------ 
// Constraints 
//------------------------------------------ 
//------------------------------------------ 
// Methods 
//------------------------------------------ 
// Standard UVM Methods: 
 extern function new(string name = "spi_env", uvm_component parent = null); 
 extern function void build_phase(uvm_phase phase);
 extern function void connect_phase(uvm_phase phase); 
endclass:spi_env 
function spi_env::new(string name = "spi_env", uvm_component parent = null); 
 super.new(name, parent); 
endfunction 
function void spi_env::build_phase(uvm_phase phase); 
 if (!uvm_config_db #(spi_env_config)::get(this, "", "spi_env_config", m_cfg)) 
`uvm_fatal("CONFIG_LOAD", "Cannot get() configuration spi_env_config from uvm_config_db. Have you set() it?") 
 uvm_config_db #(apb_agent_config)::set(this, "m_apb_agent*", "apb_agent_config", m_cfg.m_apb_agent_cfg); 
 m_apb_agent = apb_agent::type_id::create("m_apb_agent", this);
// Build the register model predictor 
 m_apb2reg_predictor = 
 uvm_reg_predictor#(apb_seq_item)::type_id::create("m_apb2reg_predictor", this); 
 m_reg2apb = reg2apb_adapter::type_id::create("m_reg2apb"); 
 uvm_config_db #(spi_agent_config)::set(this, "m_spi_agent*","spi_agent_config", m_cfg.m_spi_agent_cfg); 
 m_spi_agent = spi_agent::type_id::create("m_spi_agent", this);
 if(m_cfg.has_spi_scoreboard) begin
   m_scoreboard = spi_scoreboard::type_id::create("m_scoreboard", this); 
 end
endfunction:build_phase
function void spi_env::connect_phase(uvm_phase phase); 
// Only set up register sequencer layering if the spi_rb is the top block 
// If it isn't, then the top level environment will set up the correct sequencer 
// and predictor 
 if(m_cfg.spi_rb.get_parent() == null) begin 
   if(m_cfg.m_apb_agent_cfg.active == UVM_ACTIVE) begin 
   m_cfg.spi_rb.spi_reg_block_map.set_sequencer(m_apb_agent.m_sequencer, m_reg2apb); 
end 
// 
// Register prediction part: 
//
// Replacing implicit register model prediction with explicit prediction 
// based on APB bus activity observed by the APB agent monitor
// Set the predictor map: 
m_apb2reg_predictor.map = m_cfg.spi_rb.spi_reg_block_map;
// Set the predictor adapter: 
m_apb2reg_predictor.adapter = m_reg2apb; 
// Disable the register models auto-prediction 
m_cfg.spi_rb.spi_reg_block_map.set_auto_predict(0); 
// Connect the predictor to the bus agent monitor analysis port 
m_apb_agent.ap.connect(m_apb2reg_predictor.bus_in); 
end 
if(m_cfg.has_spi_scoreboard) begin 
 m_spi_agent.ap.connect(m_scoreboard.spi.analysis_export);
   m_scoreboard.spi_rb = m_cfg.spi_rb; 
end 
endfunction: connect_phase 

Agents

由于UVM build过程是自顶向下的,所以接下来构建SPI和APB agent。UVM_basics关于agent构建过程的部分描述了如何配置和构建APB agent,SPI agent也遵循相同的过程。

agent中的组件位于testbench层次结构的底部,因此build过程终止于此。

集成级Testbench

这个testbench示例采用了两个模块级别的验证环境,并展示了如何在更高的集成级别重用它们。示例中所说明的原则适用于重复垂直重用。

该示例采用SPI模块级示例,并将其与GPIO DUT的另一个模块级验证环境集成。这两个模块的硬件已经集成到外设子系统(PSS)中,该子系统使用“AHB2APB 总线桥”与 SPI 和 GPIO 模块上的 APB 接口连接。来自模块级别的环境由pss_env封装,它还包含一个AHB agent来驱动AHB总线接口。在此配置中,模块级APB总线接口不再出现,APB agent进入passive,监测APB传输。该激励需要驱动AHB接口,寄存器分层允许在集成级重用模块级激励。

如上文所述,这种级别下,模块环境的APB接口工作在passive模式,DUT对外的直接接口变成了AHB,虽然我们可以通过AHB接口的monitor获取信息,但是我们仍然选择保留APB monitor,可以辅助debug及做一定的检查。

现在,我们将从两个顶层testbench模块开始,从上到下依次介绍testbench和构建过程。

顶层testbench module

与模块级testbench示例一样,这里也使用了两个顶层module。hdl_top实例化DUT,实例化BFM接口,并将pin接口连接到DUT和BFM接口。引用BFM接口的虚接口句柄会被放入配置空间,并生成时钟和复位。此代码与模块级testbench代码之间的主要区别是有更多的接口,并且需要绑定到一些内部信号来监视APB总线。另一个区别是,如果要在passive模式下使用agent,便不会实例化driver BFM。DUT由一个module封装,该module将其I/O信号连接到UVM testbench使用的接口。内部信号使用binder module绑定到APB接口:

module top_tb; 
 import uvm_pkg::*; 
 import pss_test_lib_pkg::*; 
// PCLK and PRESETn 
// 
 logic HCLK; 
 logic HRESETn; 
// 
// Instantiate the pin interfaces: 
// 
 apb_if APB(HCLK, HRESETn);
// APB interface - shared between passive agents 
 ahb_if AHB(HCLK, HRESETn);
// AHB interface
 spi_if SPI();
// SPI Interface
 ...
// Additional pin interfaces
// 
// Instantiate the BFM interfaces: 
// 
apb_monitor_bfm APB_SPI_mon_bfm( 
.PCLK    (APB.PCLK),
.PRESETn (APB.PRESETn), 
.PADDR  (APB.PADDR), 
.PRDATA  (APB.PRDATA), 
.PWDATA  (APB.PWDATA), 
.PSEL  (APB.PSEL), 
.PENABLE (APB.PENABLE), 
.PWRITE  (APB.PWRITE), 
.PREADY  (APB.PREADY) 
); 
apb_monitor_bfm APB_GPIO_mon_bfm( 
.PCLK  (APB.PCLK),
.PRESETn (APB.PRESETn), 
.PADDR  (APB.PADDR), 
.PRDATA (APB.PRDATA), 
.PWDATA  (APB.PWDATA),
.PSEL  (APB.PSEL), 
.PENABLE  (APB.PENABLE), 
.PWRITE  (APB.PWRITE), 
.PREADY 
(APB.PREADY) 
); 
apb_driver_bfm APB_GPIO_drv_bfm(
  .PCLK  (APB_dummy.PCLK),
  .PRESETn (APB_dummy.PRESETn), 
.PADDR  (APB_dummy.PADDR), 
.PRDATA  (APB_dummy.PRDATA), 
(APB_dummy.PWDATA),  .PWDATA
.PSEL 
(APB_dummy.PSEL), 
.PENABLE  (APB_dummy.PENABLE), 
.PWRITE  (APB_dummy.PWRITE), 
.PREADY  (APB_dummy.PREADY) 
); 
... 
// Additional BFM interfaces 
// Binder 
binder probe(); 
// DUT Wrapper: 
pss_wrapper wrapper(.ahb(AHB), 
.spi(SPI), 
.gpi(GPI), 
.gpo(GPO), 
.gpoe(GPOE), 
.icpit(ICPIT), 
.uart_rx(UART_RX),
.uart_tx(UART_TX),
.modem(MODEM)); 
// UVM initial block: 
// Virtual interface wrapping
initial begin 
  import uvm_pkg::uvm_config_db;
    uvm_config_db  #(virtual apb_monitor_bfm)::set(null, "uvm_test_top", "APB_SPI_mon_bfm", APB_SPI_mon_bfm); 
    uvm_config_db  #(virtual apb_monitor_bfm)::set((null, "uvm_test_top", "APB_GPIO_mon_bfm", APB_GPIO_mon_bfm); 
 uvm_config_db #(virtual ahb_monitor_bfm)::set(null, "uvm_test_top", "AHB_mon_bfm",AHB_mon_bfm); 
 uvm_config_db #(virtual ahb_driver_bfm)  ::set(null, "uvm_test_top", "AHB_drv_bfm",AHB_drv_bfm); 
 uvm_config_db #(virtual spi_monitor_bfm) ::set(null, "uvm_test_top", "SPI_mon_bfm",SPI_mon_bfm); 
 uvm_config_db #(virtual spi_driver_bfm)  ::set(null, "uvm_test_top", "SPI_drv_bfm",SPI_drv_bfm);
...
//Additional uvm_config_db::set() calls
end 
// 
// Clock and reset initial block: 
// 
initial begin 
 HCLK = 1; 
 forever #10ns HCLK = ~HCLK; 
end 
initial begin 
 HRESETn = 0; 
 repeat(4) @(posedge HCLK); 
 HRESETn = 1; 
end 
// Clock assignments: 
 assign GPO.clk = HCLK; 
 assign GPOE.clk = HCLK; 
 assign GPI.clk = HCLK; 
endmodule: hdl_top 

hvl_top module与模块级别的示例基本相同。不过它现在导入的是pss_test_lib_pkg。

module hvl_top; 
 import uvm_pkg::*; 
 import pss_test_lib_pkg::*; 
// UVM initial block: 
 initial begin 
  run_test(); 
 end 
endmodule: hvl_top

Test

与模块级test一样,集成级test应该在基类中实现公共的构建和配置过程,随后的test case可以由基类派生。从示例中可以看出,这里需要进行更多的配置,所以这种需求变得更加迫切。

pss_env的配置对象包含spi_env和gpio_env的配置对象的句柄。依照顺序,子env配置对象包含其agent子组件配置对象的句柄。pss_env负责取消嵌套spi_env和gpio_env配置对象,并在其配置表中设置它们,从而进行任何必要的本地更改。接着,spi_env和gpio_env将它们的agent配置放入它们的配置表中。

pss base test如下:

// 
// Class Description: 
// 
// 
class pss_test_base extends uvm_test; 
// UVM Factory Registration Macro 
// 
 `uvm_component_utils(pss_test_base) 
//------------------------------------------ 
// Data Members 
//------------------------------------------ 
//------------------------------------------ 
// Component Members 
//------------------------------------------ 
// The environment class 
 pss_env m_env; 
// Configuration objects 
 pss_env_config m_env_cfg; 
 spi_env_config m_spi_env_cfg; 
 gpio_env_config m_gpio_env_cfg; 
 apb_agent_config m_spi_apb_agent_cfg; 
 apb_agent_config m_gpio_apb_agent_cfg; 
 ahb_agent_config m_ahb_agent_cfg; 
 spi_agent_config m_spi_agent_cfg; 
 ...
// Additional configuration object handles
// Register map 
 pss_register_map pss_rm; 
// Interrupt Utility 
 intr_util ICPIT; 
//------------------------------------------ 
// Methods 
//------------------------------------------ 
// Standard UVM Methods: 
 extern function new(string name = "spi_test_base", uvm_component parent = null); 
 extern function void build_phase( uvm_phase phase); 
 extern virtual function void configure_apb_agent(apb_agent_config cfg, int index, 
 logic[31:0] start_address, logic[31:0] range); 
 extern task run_phase( uvm_phase phase ); 
endclass: pss_test_base 
function pss_test_base::new(string name = "spi_test_base", uvm_component parent = null); 
 super.new(name, parent); 
endfunction 
// Build the env, create the env configuration 
// including any sub configurations and assigning virtural interfaces 
function void pss_test_base::build_phase(uvm_phase phase); 
 virtual intr_bfm temp_intr_bfm;
 m_env_cfg = pss_env_config::type_id::create("m_env_cfg");
// Register model 
// Enable all types of coverage available in the register model 
 uvm_reg::include_coverage("*", UVM_CVR_ALL);
// Register map - Keep reg_map a generic name for vertical reuse reasons 
 pss_rb = pss_reg_block::type_id::create("pss_rb");
 pss_rb.build(); 
 m_env_cfg.pss_rb = pss_rb;
// SPI Sub-env configuration: 
 m_spi_env_cfg = spi_env_config::type_id::create("m_spi_env_cfg"); 
 m_spi_env_cfg.spi_rb = pss_rb.spi_rb;
// apb agent in the SPI env:
 m_spi_apb_agent_cfg = apb_agent_config::type_id::create("m_spi_apb_agent_cfg"); 
 configure_apb_agent(m_spi_apb_agent_cfg, 0, 32'h0, 32'h18); 
 if (!uvm_config_db #(virtual apb_monitor_bfm)::get(this, "", "APB_SPI_mon_bfm", m_spi_apb_agent_cfg.mon_bfm)) 
   `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface APB_SPI_mon_bfm from      uvm_config_db. Have you set() it?") 
// if (!uvm_config_db #(virtual apb_driver_bfm) ::get(this, "", "APB_SPI_drv_bfm", m_spi_apb_agent_cfg.drv_bfm)) 
// `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface APB_SPI_drv_bfm from uvm_config_db. Have you set() it?")
 m_spi_apb_agent_cfg.active = UVM_PASSIVE; 
 m_spi_env_cfg.m_apb_agent_cfg = m_spi_apb_agent_cfg; 
// SPI agent: 
 m_spi_agent_cfg = spi_agent_config::type_id::create("m_spi_agent_cfg"); 
 if (!uvm_config_db #(virtual spi_monitor_bfm)::get(this, "", "SPI_mon_bfm", m_spi_agent_cfg.mon_bfm))
   `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface SPI_mon_bfm from uvm_config_db. Have you set() it?") 
 if (!uvm_config_db #(virtual spi_driver_bfm) ::get(this, "", "SPI_drv_bfm", m_spi_agent_cfg.drv_bfm)) 
   `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface SPI_drv_bfm from uvm_config_db. Have you set() it?") 
 m_spi_env_cfg.m_spi_agent_cfg = m_spi_agent_cfg; 
 m_env_cfg.m_spi_env_cfg = m_spi_env_cfg; 
 uvm_config_db #(spi_env_config)::set(this, "*", "spi_env_config", m_spi_env_cfg); 
// GPIO env configuration: 
 m_gpio_env_cfg = gpio_env_config::type_id::create("m_gpio_env_cfg"); 
 m_gpio_env_cfg.gpio_rb = pss_rb.gpio_rb; 
 m_gpio_apb_agent_cfg = apb_agent_config::type_id::create("m_gpio_apb_agent_cfg"); 
 configure_apb_agent(m_gpio_apb_agent_cfg, 1, 32'h100, 32'h124); 
 if (!uvm_config_db #(virtual apb_monitor_bfm)::get(this, "", "APB_GPIO_mon_bfm", m_gpio_apb_agent_cfg.mon_bfm)) 
   `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface APB_GPIO_mon_bfm from uvm_config_db. Have you set() it?") 
// if (!uvm_config_db #(virtual apb_driver_bfm) ::get(this, "", "APB_GPIO_drv_bfm", 
 m_gpio_apb_agent_cfg.drv_bfm)) 
// `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface APB_drv_bfm from uvm_config_db. Have you set() it?")
 m_gpio_apb_agent_cfg.active = UVM_PASSIVE; 
 m_gpio_env_cfg.m_apb_agent_cfg = m_gpio_apb_agent_cfg; 
 m_gpio_env_cfg.has_functional_coverage = 1;
// Register coverage no longer valid 
// GPO agent
 m_GPO_agent_cfg = gpio_agent_config::type_id::create("m_GPO_agent_cfg"); 
 if (!uvm_config_db #(virtual gpio_monitor_bfm)::get(this, "", "GPO_mon_bfm", m_GPO_agent_cfg.mon_bfm)) 
 `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface GPO_mon_bfm from uvm_config_db. Have you set() it?") 
// if (!uvm_config_db #(virtual gpio_driver_bfm) ::get(this, "", "GPO_drv_bfm", m_GPO_agent_cfg.drv_bfm)) 
// `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface SPI_drv_bfm from uvm_config_db. Have you set() it?")
 m_GPO_agent_cfg.active = UVM_PASSIVE;
// Only monitors
 m_gpio_env_cfg.m_GPO_agent_cfg = m_GPO_agent_cfg; 
// GPOE agent
 m_GPOE_agent_cfg = gpio_agent_config::type_id::create("m_GPOE_agent_cfg"); 
 if (!uvm_config_db #(virtual gpio_monitor_bfm)::get(this, "", "GPOE_mon_bfm", m_GPOE_agent_cfg.mon_bfm))
   `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface GPOE_mon_bfm from uvm_config_db. Have you set() it?")
// if (!uvm_config_db #(virtual gpio_driver_bfm) ::get(this, "", "GPOE_drv_bfm", m_GPOE_agent_cfg.drv_bfm))
// `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface SPI_drv_bfm from uvm_config_db. Have you set() it?")
 m_GPOE_agent_cfg.active = UVM_PASSIVE;
// Only monitors
 m_gpio_env_cfg.m_GPOE_agent_cfg = m_GPOE_agent_cfg; 
// GPI agent - active (default)
 m_GPI_agent_cfg = gpio_agent_config::type_id::create("m_GPI_agent_cfg"); 
 if (!uvm_config_db #(virtual gpio_monitor_bfm)::get(this, "", "GPI_mon_bfm", m_GPI_agent_cfg.mon_bfm)) 
   `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface GPI_mon_bfm from uvm_config_db. Have you set() it?") 
 if (!uvm_config_db #(virtual gpio_driver_bfm) ::get(this, "", "GPI_drv_bfm", m_GPI_agent_cfg.drv_bfm)) 
   `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface SPI_drv_bfm from uvm_config_db. Have you set() it?")
 m_gpio_env_cfg.m_GPI_agent_cfg = m_GPI_agent_cfg; 
// GPIO Aux agent not present 
 m_gpio_env_cfg.has_AUX_agent = 0; 
 m_gpio_env_cfg.has_functional_coverage = 1; 
 m_gpio_env_cfg.has_out_scoreboard = 1; 
 m_gpio_env_cfg.has_in_scoreboard = 1; 
 m_env_cfg.m_gpio_env_cfg = m_gpio_env_cfg; 
 uvm_config_db #(gpio_env_config)::set(this, "*", "gpio_env_config", m_gpio_env_cfg); 
// AHB Agent 
 m_ahb_agent_cfg = ahb_agent_config::type_id::create("m_ahb_agent_cfg"); 
 if (!uvm_config_db #(virtual ahb_monitor_bfm)::get(this, "", "AHB_mon_bfm", m_ahb_agent_cfg.mon_bfm)) 
   `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface AHB_mon_bfm from uvm_config_db. Have you set() it?") 
 if (!uvm_config_db #(virtual ahb_driver_bfm) ::get(this, "", "AHB_drv_bfm", m_ahb_agent_cfg.drv_bfm)) 
   `uvm_fatal("VIF CONFIG", "Cannot get() BFM interface AHB_drv_bfm from uvm_config_db. Have you set() it?")
 m_env_cfg.m_ahb_agent_cfg = m_ahb_agent_cfg;
// Add in interrupt line 
 ICPIT = intr_util::type_id::create("ICPIT"); 
 if (!uvm_config_db #(virtual intr_bfm)::get(this, "", "ICPIT_bfm", temp_intr_bfm)) 
   `uvm_fatal("VIF CONFIG", "Cannot get() interface ICPIT_bfm from uvm_config_db. Have you set() it?") 
 ICPIT.set_bfm(temp_intr_bfm); 
 m_env_cfg.ICPIT = ICPIT; 
 m_spi_env_cfg.INTR = ICPIT; 
 uvm_config_db #(pss_env_config)::set(this, "*", "pss_env_config", m_env_cfg); 
 m_env = pss_env::type_id::create("m_env", this);
endfunction: build_phase
// 
// Convenience function to configure the apb agent 
// 
// This can be overloaded by extensions to this base class 
function void pss_test_base::configure_apb_agent(apb_agent_config cfg, int index, 
logic[31:0] start_address, logic[31:0] range);
 cfg.active = UVM_PASSIVE; 
 cfg.has_functional_coverage = 0; 
 cfg.has_scoreboard = 0; 
 cfg.no_select_lines = 1;
 cfg.apb_index = index;
 cfg.start_address[0] = start_address;
 cfg.range[0] = range;
endfunction: configure_apb_agent
        
task pss_test_base::run_phase( uvm_phase phase );
endtask: run_phase 

同样,扩展这个基类的test case将填充它的run方法,以定义将在env中的virtual sequencer上运行的virtual sequence。如果要执行非默认配置,那么可以通过填充或重载build方法或任何配置方法来完成。

// 
// Class Description: 
// 
// 
class pss_spi_polling_test extends pss_test_base; 
// UVM Factory Registration Macro 
// 
 `uvm_component_utils(pss_spi_polling_test) 
//------------------------------------------ 
// Methods
//------------------------------------------ 
// Standard UVM Methods: 
 extern function new(string name = "pss_spi_polling_test", uvm_component parent = null); 
 extern function void build_phase(uvm_phase phase);
 extern task run_phase(uvm_phase phase); 
endclass: pss_spi_polling_test 
        
function pss_spi_polling_test::new(string name = "pss_spi_polling_test", uvm_component parent = null); 
 super.new(name, parent); 
endfunction 
// Build the env, create the env configuration 
// including any sub configurations and assigning virtural interfaces
function void pss_spi_polling_test::build_phase(uvm_phase phase); 
 super.build_phase(phase); 
endfunction: build_phase 
task pss_spi_polling_test::run_phase(uvm_phase phase); 
   config_polling_test t_seq = config_polling_test::type_id::create("t_seq"); 
 t_seq.m_cfg = m_spi_env_cfg; 
 t_seq.spi = m_env.m_spi_env.m_spi_agent.m_sequencer;
 phase.raise_objection(this, "Starting PSS SPI polling test"); 
 repeat(10) begin 
  t_seq.start(null); 
 end 
 phase.drop_objection(this, "Finishing PSS SPI polling test");
endtask: run_phase

PSS env

PSS env build过程在检查各种 has_<sub-component> 字段以确定test case是否需要 env 后,检索配置对象并构建各种sub-env。如果要存在 sub-envs,则在 PSS envs 配置表中设置 sub-envs 配置对象。connect 方法用于在 TLM 端口之间建立连接,并在monitor和analysis组件(如scoreboard)之间建立连接。

// 
// Class Description: 
// 
// 
class pss_env extends uvm_env; 
// UVM Factory Registration Macro 
// 
 `uvm_component_utils(pss_env) 
//------------------------------------------ 
// Data Members 
//------------------------------------------ 
 pss_env_config m_cfg;
//------------------------------------------ 
// Sub Components 
//------------------------------------------ 
 spi_env m_spi_env; gpio_env 
 m_gpio_env; ahb_agent 
 m_ahb_agent; 
// Register layer adapter 
 reg2ahb_adapter m_reg2ahb; 
// Register predictor 
 uvm_reg_predictor#(ahb_seq_item) m_ahb2reg_predictor; 
//------------------------------------------ 
// Methods 
//------------------------------------------ 
// Standard UVM Methods: 
 extern function new(string name = "pss_env", uvm_component parent = null); 
// Only required if you have sub-components 
 extern function void build_phase(uvm_phase phase); 
// Only required if you have sub-components which are connected
 extern function void connect_phase(uvm_phase phase); 
endclass: pss_env 
        
function pss_env::new(string name = "pss_env", uvm_component parent = null); 
 super.new(name, parent); 
endfunction 
// Only required if you have sub-components 
function void pss_env::build_phase(uvm_phase phase); 
 if (!uvm_config_db #(pss_env_config)::get(this, "", "pss_env_config", m_cfg) ) 
   `uvm_fatal("CONFIG_LOAD", "Cannot get() configuration pss_env_config from uvm_config_db. Have you set() it?") 
 uvm_config_db #(spi_env_config)::set(this, "m_spi_env*", "spi_env_config", m_cfg.m_spi_env_cfg); m_spi_env = spi_env::type_id::create("m_spi_env", this); 
 uvm_config_db #(gpio_env_config)::set(this, "m_gpio_env*", "gpio_env_config", m_cfg.m_gpio_env_cfg); m_gpio_env = gpio_env::type_id::create("m_gpio_env", this);
 
 uvm_config_db #(ahb_agent_config)::set(this, "m_ahb_agent*", "ahb_agent_config", m_cfg.m_ahb_agent_cfg); m_ahb_agent = ahb_agent::type_id::create("m_ahb_agent", this); 
  // Build the register model predictor 
   m_ahb2reg_predictor = uvm_reg_predictor#(ahb_seq_item)::type_id::create
  ("m_ahb2reg_predictor", this); m_reg2ahb =
   reg2ahb_adapter::type_id::create("m_reg2ahb");
endfunction: build_phase 
// Only required if you have sub-components which are connected
function void pss_env::connect_phase(uvm_phase phase); 
// Only set up register sequencer layering if the pss_rb is the top block 
// If it isn't, then the top level environment will set up the correct sequencer
// and predictor 
 if(m_cfg.pss_rb.get_parent() == null) begin
    if(m_cfg.m_ahb_agent_cfg.active == UVM_ACTIVE) begin 
    m_cfg.pss_rb.pss_map.set_sequencer(m_ahb_agent.m_sequencer, m_reg2ahb);
  end 
// 
// Register prediction part:
// 
// Replacing implicit register model prediction with explicit prediction
// based on APB bus activity observed by the APB agent monitor 
// Set the predictor map: 
  m_ahb2reg_predictor.map = m_cfg.pss_rb.pss_map; 
// Set the predictor adapter: 
  m_ahb2reg_predictor.adapter = m_reg2ahb; 
// Disable the register models auto-prediction 
  m_cfg.pss_rb.pss_map.set_auto_predict(0); 
// Connect the predictor to the bus agent monitor analysis port 
  m_ahb_agent.ap.connect(m_ahb2reg_predictor.bus_in); 
 end 
endfunction: connect_phase 

testbench层次结构的其余部分

build过程自顶向下继续,如模块级testbench示例中所示,有条件地构建sub-env,并如agent示例中所述构建sub-env中包含的agent。

进一步的集成级

进一步集成级别的垂直重用可以通过扩展PSS示例描述的流程来实现。每个集成级别都会添加另一层,因此,第2级集成环境将包含两个或多个第1级环境,而第2级env配置对象将包含第1级env配置对象的嵌套句柄。显然,在层次结构的test级别上,每一轮垂直重用的代码量都会增加,但层次结构进一步向下,配置和构建过程已经在上一代垂直分层中实现了。

双顶层架构

本书中提倡的双顶层架构可实现平台可移植性——这是使用模拟或其他硬件辅助平台进行testbench加速的基础。HDL 顶层封装了与 RTL DUT 的基于时钟周期的信号级活动直接相关的所有内容,这些活动可以在仿真中运行或映射(即合成)到硬件仿真加速器上。 HVL/TB 顶层封装了面向对象的testbench,并且总是像往常一样在仿真器上运行。

除了硬件辅助的testbench加速,当然也还有其他使用双顶层架构的好理由。具体来说,它可以方便地使用多处理器平台进行仿真,使用编译和运行时优化技术,或者应用良好的软件工程实践来创建可移植的、可配置的VIP。在一个仿真中拥有多个顶层module是符合(System)Verilog标准,这是非常普遍的。所有顶层(即未实例化的)module都被有效地视为模块层次结构顶层的隐式实例。

显然,双HDL和testbench顶层模块层次结构必须相互连接,或“绑定”在一起,以实现TB - HDL域间通信。使用如下所示的UVM配置数据库可以方便地实现这一点。

HDL top level module

对于MAC示例,top_mac_hdl模块包含MAC DUT及其相关(信号和BFM)接口、MAC MII wrapper模块和带有WISHBONE总线逻辑的WISHBONE从机存储器:

module top_mac_hdl; 
 import test_params_pkg::*; 
// WISHBONE interface instance 
// Supports up to 8 masters and up to 8 slaves 
 wishbone_bus_syscon_if wb_bus_if(); 
// BFM interface instances 
 wb_m_bus_driver_bfm wb_drv_bfm(wb_bus_if); 
 wb_bus_monitor_bfm wb_mon_bfm(wb_bus_if); 
//----------------------------------- 
// WISHBONE 0, slave 0: 000000 - 0fffff 
// this is 1 Mbytes of memory 
 wb_slave_mem #(MEM_SLAVE_SIZE) wb_s_0 ( 
 ... 
 ); 
 ... 
//----------------------------------- 
// MAC 0 
// It is WISHBONE slave 1: address range 100000 - 100fff 
// It is WISHBONE Master 0 
 eth_top mac_0 ( 
 ... 
 ); 
// Wrapper module for MAC MII interface 
 mac_mii_protocol_module #(.INTERFACE_NAME("MIIM_IF")) mii_pm( 
 ... 
 ); 
 initial begin 
// Set BFM interfaces in config space 
  import uvm_pkg::uvm_config_db; 
  uvm_config_db #(virtual wb_m_bus_driver_bfm):: set(null, "uvm_test_top", "WB_DRV_BFM", wb_drv_bfm); 
  uvm_config_db #(virtual wb_bus_monitor_bfm):: set(null, "uvm_test_top", "WB_MON_BFM", wb_mon_bfm); 
  end 
endmodule 

请注意,在initial块中使用了显式命名的uvm_config_db 导入到本地,以便将BFM接口放到UVM配置空间中,这与top_mac_hdl顶层模块开头的test参数包通配符导入有适当的区别。另一方面,不加选择的通配符导入对于test参数包是合适的,因为该包的多个元素通常会在top_mac_hdl中使用。

更要注意的是,不是像上面的代码将 BFM 接口的 uvm_config_db 注册放在 HDL 顶层模块下,而是可以类似地使用完整的跨域分层实例路径放在 HVL 顶层模块下。

initial begin 
// Set BFM interfaces in config space 
 import uvm_pkg::uvm_config_db; 
  uvm_config_db #(virtual wb_m_bus_driver_bfm):: set(null, "uvm_test_top", "WB_DRV_BFM", top_mac_hdl.wb_drv_bfm); 
  uvm_config_db #(virtual wb_bus_monitor_bfm):: set(null, "uvm_test_top", "WB_MON_BFM", top_mac_hdl.wb_mon_bfm); 
end

将它放在 HDL 端更加优雅,因为 uvm_config_db::set 调用使用 BFM 接口本地化,因此可以使用相对实例路径。此外,完整的实例路径可以使用 SystemVerilog %m 字符串格式化程序计算为字符串。例如,可以使用 $sformatf("%m.wb_drv_bfm") 而不是 "WB_DRV_BFM" 作为驱动程序 BFM 虚接口的(保证唯一的)uvm_config_db 查找键。

HVL/TB top level module

如下所示,顶层testbench模块包含一个initial块,用于调用UVM run_test()函数来启动test。注意,前面的包导入了uvm_pkg::run_test和tests_pkg::*,这是编译这个函数调用所必需的。

module top_mac_hvl; 
 import uvm_pkg::run_test; 
 import tests_pkg::*; 
 initial 
  run_test(); // create and start running test
// Optionally the initial block from above for uvm_config_db 
// registration of the HDL side BFM interfaces 
endmodule

END

本文分享自微信公众号 - 摸鱼范式(icparadigm),作者:Key1h

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-08-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【UVM COOKBOOK】Testbench Architecture【一】

    UVM testbench 是使用SystemVerilog(动态)类对象与SystemVerilog(静态)接口和结构化层次结构中的模块交互构建的。层次结构...

    空白的贝塔
  • 【UVM COOKBOOK】DUT-Testbench Connections

    UVM testbench对象不能直接连接到DUT信号来驱动或采样。driver和monitor组件对象与DUT之间的连接是通过一个或多个具有静态信号端口的BF...

    空白的贝塔
  • 【UVM COOKBOOK】配置test环境

    设计可重用testbench的关键原则之一是使其尽可能可配。这就意味着testbench及其组成部分可以很容易地重用和快速修改(即重新配置)。在testbenc...

    空白的贝塔
  • 【UVM COOKBOOK】UVM基础【二】

    testbench分析部分的第一个任务是监测DUT上的活动。和driver一样,monitor也是agent的组成部分。类似于driver组件,执行的也是实际信...

    空白的贝塔
  • 我的第二个UVM代码——连接interface

    写testbench最最核心的部分就是发激励,今天用一个简单的示例来介绍如何在uvm里给RTL发激励。

    ExASIC
  • 动静结合,了解典型的UVM验证平台架构

    典型的基于UVM 的验证平台(Testbench)通常会实例化DUT和UVM Testcase,以及完成DUT和UVM Testcase之间的链接。其中Test...

    AsicWonder
  • UVM(七)之phase及objection

    UVM(七)之phase及objection 这两个概念与UVM验证平台息息相关,phase就好比铁轨,让UVM这趟列车在铁轨上向前运行,不会脱轨,不...

    瓜大三哥
  • UVM学习--基本概念篇1

    UVM 是 Universal Verification Methodology 的缩写,即通用验证方法学。它起源于 OVM(Open Verification...

    数字IC小站
  • 深入AXI4 总线实战:Hello AXI handshake

    欢迎来到深入 AXI4 总线的实战篇,系列第二篇文章中,我们将首先了解调用 AXI VIP 产生激励与响应的方法,并完成一个小目标:实现三种情况下的握手信号。

    数字芯片社区
  • IC验证的一种最佳实践:pandora-v0.5

    在写这篇文章前有过很多的犹豫,要不要与当前大众工作方式更近似一些,不引入迭代、看板,不加入基于针对验证的单元测试、功能测试、接口测试。后来发现如果不加入就会使得...

    ExASIC
  • 数字IC设计验证-秋招指南

    现在距离正式九月正式秋招还有4个月,距离八月提前批还有3个月,相信不少粉丝都在盘算着为工作做准备。今天,我以一个过来者的身份跟大家分享分享,关于秋招如何准备以及...

    空白的贝塔
  • GPU编程(一): Ubuntu下的CUDA8.0环境搭建

    https://zh.wikipedia.org/wiki/英伟达 https://gist.github.com/dangbiao1991/7825db1d...

    sean_yang
  • [干货]用ModelSim搭建可看代码覆盖率的千兆以太网控制器的仿真环境!

    在hdl文件夹下是对应所有的设计代码,本文中选用opencores网站中十百千自适应的MAC控制器作为设计代码。

    网络交换FPGA
  • 验证仿真提速系列--SystemVerilog编码层面提速的若干策略

    随着设计复杂度和规模增加,验证平台复杂度跟着增加。验证平台的仿真速度问题成为验证过程中一个重要问题。

    IC验证
  • 极说・IC圆桌派复盘笔记之闲话DV

    说实话,我们都希望疫情可以很快过去,但真不知道何时能过去,焦虑和恐慌仍然笼罩在每个家庭的心头,2/10号复工,几家欢喜几家愁,自上班以来就没放过这么长的假,但苦...

    老秃胖驴
  • 零基础入门FPGA,如何学习?

    问:本人零基础,想学FPGA,求有经验的人说说,我应该从哪入手,应该看什么教程,应该用什么学习板和开发板,看什么书等,希望有经验的好心人能够给我一些引导。

    碎碎思
  • 【干货】2018年深度学习必读的31篇论文,赶紧收藏!

    Kloud Strife在其博客上盘点了今年最值得关注的有关深度学习的论文,包括架构/模型、生成模型、强化学习、SGD & 优化及理论等各个方面,有些论文名扬四...

    钱塘数据
  • 【干货】2017年深度学习必读31篇论文(附论文下载地址)

    摘要: 2017年即将擦肩而过,Kloud Strife在其博客上盘点了今年最值得关注的有关深度学习的论文,包括架构/模型、生成模型、强化学习、SGD & 优化...

    WZEARW
  • 2017年深度学习必读31篇论文

    一如既往,首先,标准免责声明适用,因为今年仅与GAN有关的论文就超过1660篇。我肯定会有疏漏,试图缩减到每两周一篇论文,包含了Imperial Deep Le...

    华章科技

扫码关注云+社区

领取腾讯云代金券