精华内容
下载资源
问答
  • 2022-03-03 17:20:40

    7.1.1、带寄存器配置总线的DUT

    在本书以前所有的例子中, 使用的DUT几乎都是基于2.2.1节中所示的最简单的DUT, 只有一组数据输入输出口, 而没有行为控制口, 这样的DUT几乎是没有任何价值的。 通常来说, DUT中会有一组控制端口, 通过控制端口, 可以配置DUT中的寄存器,DUT可以根据寄存器的值来改变其行为这组控制端口就是寄存器配置总线。 这样的DUT的一个示例如附录B的代码清单B-2所示。

    寄存器配置总线的DUT:

    //文件: src/ch7/dut/dut.sv
    module dut(clk
    更多相关内容
  • 模块使用python实现excel到寄存器模型文件的转换功能。使用前在Windows平台需要安装python3.x 以及openpyxl模块,使用时将需要转换的模块寄存器按照examp.xlsx格式填好excel,excel文件名设置为寄存器名字,将excel...
  • 寄存器模型生成工具,可将excel表格直接生成uvm验证使用的寄存器模型
  • Register Access without RAL Model  In this section will see an example that shows one of the ways to access DUT registers without the UVM RAL Model.Let’s consider a DMA design which consists of ...
  • 寄存器模型

    2022-03-27 16:44:27
    UVM中寄存器模型的一些概念。

    寄存器模型的概念

    寄存器配置总线:通过控制端口,配置DUT中的寄存器,DUT可以根据寄存器的值来改变其行为。

    uvm_reg_field:寄存器模型中最小的单位是具体存储寄存器数值的变量。
    uvm_reg:比uvm_reg_field高一个级别,但依然是比较小的单位。下图为uvm_reg_field与uvm_reg_的关系:
    在这里插入图片描述

    uvm_reg_block:一个比较大的单位,在其中可以加入许多的uvm_reg,也可以加入其他的uvm_reg_block,一个寄存器模型中至少包含一个uvm_reg_block。
    uvm_reg_map:每个寄存器在加入寄存器模型时都有其地址,uvm_reg_map就是存储这些地址,并将其转换成可以访问的物理地址。

    建造只有一个寄存器的寄存器模型

    class reg_invert extends uvm_reg;
    	rand uvm_reg_field reg_data;
    	virtual function void build();
    		reg_data = uvm_reg_field::type_id::create("reg_data");
    		reg_data.configure(this, 1, 0, "RW", 1, 0, 1, 1, 0);
    	endfunction
    	`uvm_object_utils(reg_invert)
    	function new(input string name = "reg_invert");
    		super.new(name, 16, UVM_NO_COVERAGE);
    	endfunction
    endclass
    
    • new函数中的位数一般与系统总线的宽度一致,另外一个参数为是否要加入覆盖率的支持,这里选择不支持。
    • 每一个派生自uvm_reg的类都有一个build,所有的uvm_reg_field都在这里实例化,当reg_data实例化后,要调用reg_data.configure函数来配置这个字段。
      定义好这个寄存器后,需要在一个由reg_block派生的类中将其实例化:
    class reg_model enxtends uvm_reg_block;
    	rand reg_invert invert;
    	virtual function void build();
    		default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
    		invert = reg_invert::type_id::create("invert",  , get_full_name());
    		invert.configure(this, null, " ");
    		invert.build();
    		default_map.add_reg(invert, 'h9', "RW");
    	endfucntion
    	`uvm_object_utils(reg_model)
    	function new(input string name = "reg_model");
    		super.new(name, UVM_NO_COVERAGE);
    	endfunction
    endclass
    

    同reg_reg派生的类一样,每一个由uvm_reg_block派生的类也要定义一个build函数,一般在此函数中实现所有寄存器的例化。

    • 一个uvm_reg_block中一定要对应一个uvm_reg_map,通过调用uvm_reg_block的create_map在build中将其实例化。create_map的参数中,第一个参数是名字,第二个参数是基地址,第三个参数是系统总线的宽度(byte),第四个参数是大小端,最后一个参数表示是否能够按照byte进行寻址。
    • 随后实例化invert并调用invert.configure函数。这个函数的主要功能是指定寄存器进行后门访问操作时的路径。第一个参数是此寄存器所在uvm_reg_block指针,这里填写this;第二个参数是reg_file的指针;第三个参数是此寄存器的后门访问路径,这里暂且为空。当调用完configure时,需要手动调用invert的build函数,将invert中的域实例化。
    • 最后一步是将此寄存器加入default_map中,uvm_reg_map的作用是存储所有寄存器的地址,因此必须将实例化的寄存器加入default_map中,否则无法进行前门访问操作。add_reg函数的第一个参数是要加入的寄存器,第二个参数是寄存器的地址,第三个参数是此寄存器的存取方式。

    寄存器模型的集成

    寄存器模型的前门访问操作可以分成读和写两种。无论是读或写,寄存器模型都会通过sequence产生一个uvm_reg_bus_op的变量,此变量中存储着操作类型(读还是写)和操作的地址,如果是写操作,还会有要写入的数据。此变量中的信息要经过一个转换器(adapter)转换后交给bus_sequencer,随后交给bus_driver,由bus_driver实现最终的前门访问读写操作。因此,必须要定义一个adapter。
    在这里插入图片描述

    在adapter中需要定义两个函数:

    • reg2bus:将寄存器模型通过sequence发出的uvm_reg_bus_op型的变量转换成bus_sequencer能够接受的形式。
    • bus2reg:当监测到总线上有操作时,将收集来的transaction转换成寄存器模型能够接受的形式。
      定义好adapter后,在base_test中加入寄存器模型:
    class base_test extends uvm_test;
    	my_env 			env;
    	my_vsqr 		v_srq;
    	reg_model 		rm;
    	my_adapter 		reg_sqr_adapter;
    ...
    endclass
    function void base_test::build_phase(uvm_phase phase);
    	super.build_phase(phase);
    	env = my_env::type_id::create("env", this);
    	v_sqr = my_vsqr::typr_id::create("v_sqr", this);
    	rm = reg_model::typr_id::create("rm", this);
    	rm.configure(null, " ");
    	rm.build();
    	rm.lock_build();
    	rm.reset();
    	reg_sqr_adapter = new("reg_sqr_adapter");
    	env.p_rm = this.rm;
    endfunction
    
    function void base_test::connect_phase(uvm_phase phase);
    	super.connect_phase(phase);
    	v_sqr.p_my_sqr = env.i_agt.sqr;
    	v_sqr.p_bus_sqr = env.bus_agt.sqr;
    	v_sqr.p_rm = this.rm;
    	rm.default_map.set_sequencer(env.bus_agt.sqr, reg_sqr_adapter);
    	rm.default_map.set_auto_predoct(1);
    endfunction
    

    要将一个寄存器模型集成到base_test中,至少需要在base_test中定义reg_model和reg_sqr_adapter。将所有用到的类在build_phase中例化,在例化后reg_model还要做四件事:

    1. 调用configure函数,第一个参数是parent block,由于是最顶层的reg_block,因此填null;第二个参数是后门访问路径,这里传入一个空的字符串。
    2. 调用build函数,将所有的寄存器实例化。
    3. 调用lock_model函数,调用此函数后,reg_model中就不能再加入新的寄存器了。
    4. 调用reset函数,如果不调用此函数,那么reg_model中所有寄存器的值都是0。调用此函数后,所有寄存器的值都将变为设置的复位值。
      寄存器模型的前门访问操作最终都将由uvm_reg_map完成,因此在connect_phase中,需要将adapter和bus_sequencer通过set_sequencer函数告知reg_model的default_map,并将default_map设置为自动预测状态。

    寄存器模型的使用

    寄存器模型提供了read和write两个task,对于read:

    p_rm.invert.read(status, value, UVM_FRONTDOOR);
    

    read的第一个参数为uvm_status_e型的变量,作为一个输出,其用于表明读操作是否成功;第二个参数是读取的数值;第三个是读取的方式,可选UVM_FRONTDOOR和UVM_BACKDOOR。
    对于write:

    p_sequencer.p_rm.invert.write(status, 1, UVM_FRONTDOOR);
    

    第一个参数也是uvm_status_e型的变量,用于表明写操作是否成9功;第二个参数是要写的值;第三个参数是写操作的方式,同样可选UVM_FRONTDOOR和UVM_BACKDOOR。
    寄存器模型对sequence的transaction类型没有任何要求,所以可以在一个发送my_transaction的sequence中使用寄存器模型对寄存器进行读写操作。

    后门访问与前门访问

    • 前门访问:通过寄存器配置总线来对DUT进行操作。在这个过程中,仿真时间是一直往前走的。
      通过adapter的bus2reg及reg2bus,可以实现uvm_reg_item与目标transaction的转换,以读操作为例,完整的流程为:
    1. 参考模型调用寄存器模型的读任务。
    2. 寄存器模型产生sequence,并产生uvm_reg_item:rw。
    3. 产生driver能够接受的transaction:bus_req=adapter.reg2bus(rw)。
    4. 把bus_req交给bus_sequencer。
    5. driver得到bus_req后驱动它,得到读取的值,并将读取值放入bus_req中,调用item_done。
    6. 寄存器模型调用adapter.bus2reg(bus_req,rw)将bus_req中的读取值传递给rw。
    7. 将rw中的读数据返回参考模型。
    • 后门访问:不通过总线进行读写操作,而是直接通过层次化的引用来改变寄存器的值。所有的后门访问都是不消耗仿真时间而只消耗运行时间的。

      可以使用interface以及DPI+VPI的方式来进行后门访问。
      UVM中使用DPI+VPI的方式来进行后门访问操作,它大体的流程是:
      1)在建立寄存器模型时将路径参数设置好。
      2)在进行后门访问的写操作时,寄存器模型调用uvm_hdl_deposit函数。
      3)进行后门访问的读操作时,调用uvm_hdl_read函数,在C/C++侧,此函数内部会调用vpi_get_value函数来对DUT中的寄存器进行读操作,并将读取值返回。

      在使用寄存器模型的后门访问功能时,需要做如下准备:
      (1)在reg_block中调用uvm_reg的configure函数时,需要设置好第三个参数:

    class reg_model extends uvm_reg_block;
    	rand reg_invert invert;
    	rand reg_counter_high counter_high;
    	rand reg_counter_low counter_low;
    	virtual function void build();
    		invert.configure(this, null, "invert");
    		counter_high.configure(this, null, "counter[31:16]");
    		counter_low.configure(this, null, "counter[15:0]");
    	endfunction
    endclass
    

    (2)在将寄存器模型集成到验证平台时,需要设置好根路径hdl_root:

    function void base_test::build_phase(uvm_phase phase);
    ...
    	rm = reg_model::type_id::create("rm", this);
    	rm.configure(null, " ");
    	rm.build();
    	rm.lock_model();
    	rm.reset();
    	rm.set_hdl_path_root("top_tb.my_dut");
    ...
    endfunction
    

    UVM会提供两类后门访问的函数,一类是read和write,一类是peek和poke,区别在于:第一类在进行操作时会模仿DUT的行为,第二类则完全不管DUT的行为。例如,对一个只读寄存器进行写操作,第一类由于要模拟DUT的只读行为所以写不进去,这时就可以用第二类去写。

    p_sequencer.p_rm.counter_low.poke(status, 16'hFFFD);
    p_sequencer.p_rm.counter_low.peek(status, value);
    

    poke和peek的第一个参数表示操作是否成功,第二个参数表示读写的数据。

    复杂的寄存器模型

    层次化的寄存器模型

    一般只会在第一级的uvm_reg_block中加入寄存器,而第二级的uvm_reg_block通常只添加uvm_reg_block,这样从整体上就能呈现出如下图中比较清晰的结构。
    在这里插入图片描述
    例如,一个DUT分了三个子模块:用于控制全局的global模块、用于缓存数据的buf模块、用于接收发送以太网帧的mac模块。global模块寄存器的地址为0x0000~0x0FFF,buf部分的寄存器地址为0x1000~0x1FFF,mac部分的寄存器地址为0x2000~0x2FFF,那么可以按照如下方式定义寄存器模型:

    class reg_model extends uvm_reg_block;
    
    	rand global_blk gb_ins;
    	rand buf_blk bb_ins;
    	rand mac_blk mb_ins;
    	virtual function void build();
    		default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
    		gb_ins = global_blk::type_id::create("gb_ins");
    		gb_ins.configure(this, "");
    		gb_ins.build();
    		gb_ins.lock_model();
    		default_map.add_submap(gb_ins.default_map, 16'h0);
    
    		bb_ins = buf_blk::type_id::create("bb_ins");
    		bb_ins.configure(this, "");
    		bb_ins.build();
    		bb_ins.lock_model();
    		default_map.add_submap(bb_ins.default_map, 16'h1000);
    
    		mb_ins = mac_blk::type_id::create("mb_ins");
    		mb_ins.configure(this, "");
    		mb_ins.build();
    		mb_ins.lock_model();
    		default_map.add_submap(mb_ins.default_map, 16'h2000);
    
    	endfunction
    
    	`uvm_object_utils(reg_model)
    
    	function new(input string name="reg_model");
    		super.new(name, UVM_NO_COVERAGE);
    	endfunction
    
    endclass
    

    要将一个子reg_block加入父reg_block中,第一步是先实例化子reg_block。第二步是调用子reg_block的configure函数。第三步是调用子reg_block的build函数。第四步是调用子reg_block的lock_model函数。第五步则是将子reg_block的default_map以子map的形式加入父reg_block的default_map中。

    reg_file

    uvm_reg_file的引入主要是为了区分不同的hdl路径。

    class regfile extends uvm_reg_file;
    	function new(string name = "regfile");
    		super.new(name);
    	endfunction
    
    	`uvm_object_utils(regfile)
    endclass
    
    class mac_blk extends uvm_reg_block;
    
    	rand regfile file_a;
    	rand regfile file_b;
    	rand reg_regA regA;
    	rand reg_regB regB;
    	rand reg_vlan vlan;
    
    	virtual function void build();
    		default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
    		file_a = regfile::type_id::create("file_a", , get_full_name());
    		file_a.configure(this, null, "fileA");
    		file_b = regfile::type_id::create("file_b", , get_full_name());
    		file_b.configure(this, null, "fileB");
    		regA.configure(this, file_a, "regA");
    		regB.configure(this, file_b, "regB");
    	endfunction
    endclass
    

    如上所示,先从uvm_reg_file派生一个类,然后在mac_blk中实例化此类,之后调用其configure函数,此函数的第一个参数是其所在的reg_block的指针;第二个参数是假设此reg_file是另外一个reg_file的父文件,那么这里就填写其父reg_file的指针(由于这里
    只有这一级reg_file,因此填写null);第三个参数则是此reg_file的hdl路径。
    当把reg_file定义好后,在调用寄存器的configure参数时,就可以将其第二个参数设为reg_file的指针。

    存储器

    在寄存器模型中加入存储器的代码如下:

    class my_memory extends uvm_mem;
    	function new(string name="my_memory");
    		super.new(name, 1024, 16);
    	endfunction
    
    	`uvm_object_utils(my_memory)
    endclass
    
    class reg_model extends uvm_reg_block;
    …
    	rand my_memory mm;
    
    	virtual function void build();
    …
    		mm = my_memory::type_id::create("mm", , get_full_name());
    		mm.configure(this, "stat_blk.ram1024x16_inst.array");
    		default_map.add_mem(mm, 'h100);
    	endfunction
    …
    endclass
    
    • 首先由uvm_mem派生一个类my_memory,在其new函数中调用super.new函数。这个函数有三个参数,第一个是名字,第二个是存储器的深度,第三个是宽度。
    • 然后在reg_model的build函数中,将存储器实例化,调用其configure函数,第一个参数是所在reg_block的指针,第二个参数是此块存储器的hdl路径。
    • 最后调用default_map.add_mem函数,将此块存储器加入default_map中,从而可以对其进行前门访问操作。如果没有对此块存储器分配地址空间,那么这里可以不将其加入default_map中。在这种情况下,只能使用后门访问的方式对其进行访问。

    寄存器模型对DUT的模拟

    期望值与镜像值

    • 镜像值(mirrored value):寄存器模型中的一个专门的变量,用于最大可能地与DUT保持同步。
    • 期望值(desired value):除了DUT的镜像值外,寄存器模型中还有期望值。如目前DUT中invert的值为’h0,寄存器模型中的镜像值也为’h0,但是希望向此寄存器中写入一个’h1,此时’h1便是期望值。
      一种方法是直接调用write任务,将’h1写入,期望值与镜像值都更新为’h1;另外一种方法是通过set函数将期望值设置为’h1(此时镜像值依然为0),之后调用update任务,update任务会检查期望值和镜像值是否一致,如果不一致,那么将会把期望值写入DUT中,并且更新镜像值。
    • 通过get函数可以得到寄存器的期望值,通过get_mirrored_value可以得到其镜像值。
    value = p_sequencer.p_rm.invert.get();
    value = p_sequencer.p_rm.invert.get_mirrored_value();
    

    常用操作

    • read&write操作:无论通过后门访问还是前门访问的方式从DUT中读取或写入寄存器的值,在操作完成后,寄存器模型都会根据读写的结果更新期望值和镜像值(二者相等)。
    • peek&poke操作:在操作完成后,寄存器模型会根据操作的结果更新期望值和镜像值(二者相等)。
    • get&set操作:set操作会更新期望值,但是镜像值不会改变。get操作会返回寄存器模型中当前寄存器的期望值。
    • update操作:这个操作会检查寄存器的期望值和镜像值是否一致,如果不一致,那么就会将期望值写入DUT中,并且更新镜像值,使其与期望值一致。
    • randomize操作:寄存器模型提供randomize接口。randomize之后,期望值将会变为随机出的数值,镜像值不会改变。但是并不是寄存器模型中所有寄存器都支持此函数。如果不支持,则randomize调用后其期望值不变。

    其他用法

    reg_predictor

    在这里插入图片描述

    除了像上图中左边一样使用driver的返回值更新寄存器模型以外,还可以像右边一样由monitor将从总线上收集到的transaction交给寄存器模型。
    在使用右边这种方式更新数据时,需要例化一个reg_predictor,并为这个reg_predictor实例化一个adapter:

    class base_test extends uvm_test;reg_model rm;
    	my_adapter reg_sqr_adapter;
    	my_adapter mon_reg_adapter;
    
    	uvm_reg_predictor#(bus_transaction) reg_predictor;
    …
    endclass
    
    function void base_test::build_phase(uvm_phase phase);
    …
    	rm = reg_model::type_id::create("rm", this);
    	rm.configure(null, "");
    	rm.build();
    	rm.lock_model();
    	rm.reset();
    	reg_sqr_adapter = new("reg_sqr_adapter");
    	mon_reg_adapter = new("mon_reg_adapter");
    	reg_predictor = new("reg_predictor", this);
    	env.p_rm = this.rm;
    endfunction
    
    function void base_test::connect_phase(uvm_phase phase);
    …
    	rm.default_map.set_sequencer(env.bus_agt.sqr, reg_sqr_adapter);
    	rm.default_map.set_auto_predict(1);
    	reg_predictor.map = rm.default_map;
    	reg_predictor.adapter = mon_reg_adapter;
    	env.bus_agt.ap.connect(reg_predictor.bus_in);
    endfunction
    

    在connect_phase中,需要将reg_predictor和bus_agt的ap口连接在一起,并设置reg_predictor的adapter和map。只有设置了map后,才能将predictor和寄存器模型关联在一起。
    当总线上只有一个master时,则上图中的左边和右边是完全等价的。如果有多个主设备,则左边会漏掉某些transaction。

    mirror操作

    UVM提供mirror操作,用于读取DUT中寄存器的值并将它们更新到寄存器模型中。
    其有两种应用场景,一是在仿真中不断地调用它,使得到整个寄存器模型的值与DUT中寄存器的值保持一致,此时check选项是关闭的。二是在仿真即将结束时,检查DUT中寄存器的值与寄存器模型中寄存器的镜像值是否一致,这种情况下,check选项是打开的。

    (mirror更新的是寄存器模型,update更新的是DUT。)

    展开全文
  • 简单的寄存器模型~

    7.2.1、只有一个寄存器的寄存器模型

    本节为7.1.1节所示的DUT建立寄存器模型。 这个DUT非常简单, 它只有一个寄存器invert。 要为其建造寄存器模型, 首先要从uvm_reg派生一个invert类:

    代码清单 7-7

    文件: src/ch7/section7.2/reg_model.sv
    class reg_invert extends uvm_reg;
    
        rand uvm_reg_field reg_data;
    
        virtual function void build
    展开全文
  • UVM——寄存器模型

    2022-04-03 21:17:09
    寄存器模型概览 硬件中的各个功能模块可以由处理器来配置功能以及访问状态,而与处理器的对话即是通过寄存器的读写来实现的。 寄存器的硬件实现是通过触发器,而每一个比特位的触发器都对应着寄存器的功能描述...

    寄存器模型概览

    硬件中的各个功能模块可以由处理器来配置功能以及访问状态,而与处理器的对话即是通过寄存器的读写来实现的。
    寄存器的硬件实现是通过触发器,而每一个比特位的触发器都对应着寄存器的功能描述(function specification)。一个寄存器一般由32个比特位构成,将单个寄存器拆分后,又可以分为多个域(field),不同的域往往代表着某一项独立的功能。单个的域可能由多个比特位构成,也可能有单一比特位构成,这取决于该域的功能模式可配置的数量。
    而不同的域,对于外部的读写而言,又大致可以分为WO(write-only,只写),RO(read-only,只读), RW(read and write,读写),除了这些常见的操作属性以外,还有一些特殊行为(quirky)的寄存器,例如读后擦除模式(clean-on-read,RC),只写一次模式(write-one-to-set,W1S)。
    在这里插入图片描述
    MCDF的寄存器模块描述,将0x00功能寄存器和0x10状态寄存器位用图来表示。通常来讲,一个寄存器有32位宽,寄存器按照地址索引的关系式按字对其(word-align)的,上图中的寄存器有多个域,每个域的属性也可以不相同,reserved域表示的是该域所包含的比特位暂时保留以作日后功能的扩展使用,而对保留域的读写不起任何作用,即无法写入而且读出值也是它的复位值。
    上面的这些寄存器按照地址排列,即可构成寄存器列表,我们称之为寄存器块(register block)。实际上寄存器模块除了包含寄存器,也可以包含存储器,因为它们的属性都近乎于读写功能,以及表示为同外界通信的接口。
    将这些寄存器有机的组合在一起,MCDF的寄存器功能模块即可由这样一个register block来表示:
    在这里插入图片描述
    一个寄存器可以由多个域构成,而单个域可以包含多个比特位;一个功能模块中的多个寄存器可以组团构成一个寄存器模型(register model)。上图中除了包含DUT的寄存器模块(由硬件实现),还有属于验证环境的寄存器模型。
    这两个模块包含的寄存器信息是高度一致的,属于验证环境的寄存器模型也可以抽象出一个层次化的寄存器列表,该列表所包含的地址、域、属性等信息都与硬件一侧的寄存器内容一致。
    对于功能验证而言,可以将总线访问寄存器的方式抽象为寄存器模型访问的方式,这种方式使得寄存器后期的地址修改(例如基地址更改)或者域的添加都不会对已有的激励构成影响,从而提高已有的测试序列的复用性。

    中心化管理方式

    在这里插入图片描述
    中心化管理的寄存器描述文件,可以使通过软件建立寄存器模型和硬件寄存器的内容属性保持一致,通常使用XML格式的寄存器描述文件,或Execl(CSV)和DOC格式。采用中心化管理的原因是单一源的管理方式可以尽量降低出现分歧或错误的可能。
    寄存器描述文档使用了结构化的文档描述方式,通过数据结构化的存储方式,可以在硬件和软件开发过程中以不同的方式来使用寄存器描述文档:
    - 系统工程师会撰写并维护寄存器描述文件,而后归置到中心化存储路径供其他的工程师使用。
    - 硬件工程师会利用寄存器描述文档生成寄存器硬件模块(包含各个寄存器的硬件实现和总线访问模块)。
    - 验证工程师会利用寄存器描述文档来生成UVM寄存器模型,一共验证过程中的激励使用、寄存器测试和功能覆盖率收集。
    - 软件工程师会利用该文件生成用于软件开发的寄存器配置的头文件(header file),从而提高软件开发的可维护性。
    - 寄存器描述文件也可以用来生成文档,实现更好的可读性。

    uvm_reg相关概念

    在构建UVM寄存器模型的过程中,需要了解与模型构建相关的类和他们的功能:

    类名功能
    uvm_reg_field用来针对寄存器功能域来构建对应的比特位
    uvm_reg与寄存器相匹配,其内部可以例化和配置多个uvm_reg_field对象
    uvm_mem匹配硬件存储模型
    uvm_reg_map用来指定寄存器列表中各个寄存器的偏移地址访问属性以及对应的总线
    uvm_reg_block可以容纳多个寄存器(uvm_reg)、存储器(uvm_mem),寄存器列表(uvm_reg_map)

    MCDF寄存器模型实例

    class ctrl_reg extends uvm_reg;
    	`uvm_object_utils(ctrl_reg)
    	uvm_reg_field reserved;
    	rand uvm_reg_field pkt_len;	
    	rand uvm_reg_field prio_level;
    	rand uvm_reg_field chnl_en;
    	function new(string name = "ctrl_reg");
    		super.new(name, 32, UVM_NO_CONVERAGE);
    	endfunction
    	virtual function build();
    		reserved = uvm_reg_field::type_id::create("reserved");
    		pkt_len = uvm_reg_field::type_id::create("pkt_len");
    		prio_level = uvm_reg_field::type_id::create("prio_level");
    		chnl_en = uvm_reg_field::type_id::create("chnl_en");
    		reserved.configure(this, 26, 6, "RO", 0, 26'h0, 1, 0, 0);	//第二个参数:表明位数是多少;第三个参数:表明field从哪一位开始;第四个参数:表明field属性;第六个参数表明reset值
    		pkt_len.configure(this, 3, 3, "RW", 0, 3'h0, 1, 1, 0);
    		prio_level.configure(this, 2, 1, "RW", 0, 2'h3, 1, 1, 0);
    		chnl_en.configure(this, 1, 0, "RW", 0, 1'h0, 1, 1, 0);
    	endfunction
    endclass
    
    class stat_reg extends uvm_reg;
    	`uvm_object_utils(stat_reg)
    	uvm_reg_field reserved;
    	rand uvm_reg_field fifo_avail;
    	function new(string name = "stat_reg");
    		super.new(name, 32, UVM_ON_COVERAGE);
    	endfunction
    	virtual function build();
    		reserved = uvm_reg_field::type_id::create("reserved");
    		fifo_avail = uvm_reg_field::type_id::create("fifo_avail");
    		reserved.configure(this, 24, 8, "RO", 0, 24'h0, 1, 0, 0);
    		fifo_avail.configure(this, 8, 0, "RO", 0, 8'h0, 1, 1, 0);
    	endfunction
    endclass
    
    class mcdf_rgm extends uvm_reg_block;
    	`uvm_object_utils(mcdf_rgm)
    	rand ctrl_reg chnl0_ctrl_reg;
    	rand ctrl_reg chnl1_ctrl_reg;
    	rand ctrl_reg chnl2_ctrl_reg;
    	rand ctrl_reg chnl0_stat_reg;
    	rand ctrl_reg chnl1_stat_reg;
    	rand ctrl_reg chnl2_stat_reg;
    	uvm_reg_map map;
    	function new(string name = "mcdf_rgm");
    		super.new(name, UVM_ON_COVERAGE);
    	endfunction
    	virtual function build();
    		chnl0_ctrl_reg = ctrl_reg::type_id::create("chnl0_ctrl_reg");
    		chnl0_ctrl_reg.configure(this);
    		chnl0_ctrl_reg.build();
    		chnl1_ctrl_reg = ctrl_reg::type_id::create("chnl1_ctrl_reg");
    		chnl1_ctrl_reg.configure(this);
    		chnl1_ctrl_reg.build();
    		chnl2_ctrl_reg = ctrl_reg::type_id::create("chnl2_ctrl_reg");
    		chnl2_ctrl_reg.configure(this);
    		chnl2_ctrl_reg.build();
    		chnl0_stat_reg = stat_reg::type_id::create("chnl0_stat_reg");
    		chnl0_stat_reg.configure(this);
    		chnl0_stat_reg.build();
    		chnl1_stat_reg = stat_reg::type_id::create("chnl1_stat_reg");
    		chnl1_stat_reg.configure(this);
    		chnl1_stat_reg.build();
    		chnl2_stat_reg = stat_reg::type_id::create("chnl2_stat_reg");
    		chnl2_stat_reg.configure(this);
    		chnl2_stat_reg.build();
    		//map name, offset, number of bytes, endianess
    		map = create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);
    		map.add_reg(chnl0_ctrl_reg, 32'h00000000, "RW");
    		map.add_reg(chnl1_ctrl_reg, 32'h00000004, "RW");
    		map.add_reg(chnl2_ctrl_reg, 32'h00000008, "RW");
    		map.add_reg(chnl0_stat_reg, 32'h00000010, "RO");
    		map.add_reg(chnl1_stat_reg, 32'h00000014, "RO");
    		map.add_reg(chnl2_stat_reg, 32'h00000018, "RO");
    		lock_model();
    	endfunction
    endclass: mcdf_rgm
    

    在此整理出寄存器建模的基本要点和顺序:
    - 在定义单个寄存器时,需要将寄存器的各个域整理出来,在创建之后还应当通过uvm_reg_field::configure()函数来进一步配置各自属性。
    - 在定义uvm_reg_block时,需要注意reg_block与uvm_mem、uvm_reg、uvm_reg_map的包含关系。首先uvm_reg和uvm_mem分别对应着硬件中独立的寄存器或者存储,而一个uvm_reg_block可以用来模拟一个功能模块的寄存器模型,其中可以容纳多个uvm_reg和uvm_mem实例;其次map的作用一方面用来表示寄存器和存储对应的偏移地址,同时由于一个reg_block可以包含多个map,各个map可以分别对应不同总线或者不同地址段。在reg_block中创建了各个uvm_reg之后,需要调用uvm_reg::configure()去配置各个uvm_reg实例的属性。
    - 考虑到uvm_reg_map也会在uvm_reg_block中例化,在例化之后需要通过uvm_reg_map::add_reg()函数来添加各个uvm_reg对应的偏移地址和访问属性等。只有规定了这些属性,才可以在稍后的前门访问(frontdoor)中给出正确的地址。
    - uvm_reg_block可以对更大的系统做寄存器模型,这意味着uvm_reg_block之间也可以存在层次关系,上层uvm_reg_block的uvm_reg_map可以添加子一级uvm_reg_block的uvm_reg_map,用来构建更全局的“版图”,继而通过uvm_reg_block与uvm_reg_map之间的层次关系来构建更系统的寄存器模型。

    几个问题

    ①为什么在uvm_reg类,uvm_reg_block声明reg_field时,要声明rand?
    对于uvm_reg_block,在我们需要做randmoize时,会让uvm_reg的句柄作随机化,这就意味着句柄指向的对象uvm_reg要做随机化,进一步要求uvm_reg里的uvm_reg_field做随机化。
    ②在定义ctrl_reg类时,为什么有build(),和build_phase有什么关系?
    和build_phase没有关系。在uvm_object中不存在phase,因此我们构建一个虚函数build,允许在这里面构建reg_field。
    ③uvm_reg_block的virtual function build()中,为什么要调用各个reg的build函数?
    对于component来说,uvm_root可以帮助自动实现层层调用的方法,但是object没有,需要手动调用。

    模型使用流程

    在这里插入图片描述

    展开全文
  • 文章目录前言UVM中加入寄存器模型的方法总结 前言 这里主要记录一下,在UVM中加入寄存器模型的方法步骤。 UVM中加入寄存器模型的方法 步骤1:保证前门访问正常。在没有寄存器模型的情况下,,先保证通过寄存器...
  • UVM--寄存器模型概览

    2022-04-27 22:18:34
    寄存器概览 ...
  • 寄存器模型 — UVM

    千次阅读 2021-06-19 11:11:08
    文章目录基本概念寄存器模型的集成访问寄存器的方式1. 前门访问2. 后门访问3.前门访问和后门访问的比较寄存器模型的常规方法1. mirror、desired 和 actual value2. prediction的分类3. uvm_reg 的访问方法4. uvm_mem...
  • UVM:寄存器模型 构建

    2022-03-10 14:05:51
    寄存器模型2. 寄存器描述文件的中心化管理2.1. 寄存器生成器3. UVM 寄存器模型2.1. uvm_reg_fieldconfigure()2.2. uvm_regnew()build()configure()2.3. uvm_reg_mapadd_reg()configure()2.4. uvm_reg_blockbuild()...
  • uvm-寄存器模型

    2022-01-12 11:20:09
    寄存器模型中的基本概念2.配置寄存器模型3.将寄存器模型集成到验证平台中 UVM寄存器模型的本质就是重新定义了验证平台与DUT的寄存器接口,使得验证人员更好地组织及配置寄存器,简化流程、减少工作量。 1.寄存器...
  • 寄存器模型-1

    2022-03-19 09:41:18
    寄存器模型 寄存器模型是硬件在验证平台中的映射,一方面我们可以通过读取寄存器的状态获取硬件在当前工作状态下的情况,同时可以通过在平台配置寄存器,使得寄存器可以在一定的工作模式下工作。 ...
  • DUT的寄存器同理,通过配置不同的寄存器可以让DUT实现不同的功能,亦或者是DUT的一些状态可以通过访问对应的寄存器而得知;这些寄存器在DUT中都有相对应的地址和读写权限,通过地址可以实现对应...
  • UVM知识点总结-寄存器模型

    千次阅读 2022-04-05 13:22:28
    UVM中的寄存器模型 寄存器模型的优势:在没有寄存器模型之前,只能启动 sequence 通过前门(FRONTDOOR)访问的方式来读取寄存器,局限较大,在 scoreboard(或者其他 component )中难以控制。而有了寄存器模型之后...
  • 寄存器模型(1)

    2021-08-07 18:37:39
    寄存器 以下寄存器描述: 硬件中的各个功能模块可以由处理器来配置功能以及访问状态,而与处理器的对话即是通过寄存器的读写来实现的。 寄存器的硬件实现是通过触发器,而每一个比特位的触发器都对应着寄存器的功能...
  • 目录寄存器模型概览中心化管理方式uvm_reg相关概念MCDF寄存器模型寄存器建模模型使用流程 寄存器模型概览 寄存器是模块间互相交谈的窗口,一方面可通过读出寄存器的状态获取硬件当前的状况,另一方面也可通过配置...
  • 目录寄存器模型的常规方法mirror、desired和actual valueprediction的分类自动预测显式预测uvm_reg的访问方法mem与reg的联系和差别内建(built-in)sequences寄存器模型内建序列存储模型内建序列寄存器健康检查...
  • UVM:寄存器模型 应用

    2022-03-14 15:06:56
    最后的一部分内容,我们讨论如何检查寄存器模型对不对,以及如何使用寄存器模型惠及验证平台, 1. 高可读性 这个优势是我们一开始就提出来的,也是显而易见的。 例如,没有寄存器模型时,我们的读写是这样式的: ...
  • UVM的寄存器模型

    2022-01-05 10:23:48
    概述 寄存器 我们知道寄存器是模块之间交谈的窗口,一方面可以通过...UVM的寄存器模型是一组高级抽象的类,用来对DUT中具有地址映射的寄存器和存储器进行建模。它非常贴切的反映DUT中寄存器的各种特性,可以产生激
  • 通过寄存器模型的常规方法,可以用来检查寄存器,以及协助检查硬件设计逻辑进和比对数据。 在软件实现硬件驱动和固件层时,也会实现类似寄存器模型镜像值的方法,即在寄存器配置的底层函数中,同时也声明一些全局的...
  • component与component直接可以使用port进行通信,component与object...后门访问是可以直接层次引用,使得访问寄存器中比较简单。 二、register model的使用 三、 前门使用adapter转换,嫁给你trans转换 REG2BUS: ...
  • 寄存器模型概览

    千次阅读 2021-03-05 14:24:57
    一、寄存器模型概览 寄存器是模块之间互相交谈的窗口,一方面可以通过读出寄存器的状态,获取硬件当前的状况,另外一方面也可以通过配置寄存器,使得寄存器工作在一定的模式下。在验证的过程中,寄存器的验证也排在...
  • 寄存器模型的常规方法

    千次阅读 2021-03-06 19:19:26
    在应用寄存器模型的时候,除了利用它的寄存器信息,也会利用它来跟踪寄存器的值。寄存器模型中的每一个寄存器,都应该有两个值,一个是镜像值(mirrored value),一个时期望值(desired value)。期望值是先利用寄存器...
  • UVM寄存器模型(一) —— ralgen脚本

    千次阅读 2021-07-09 17:52:05
    文章目录 前言 一、uvm寄存器模型的生成命令 二、ralgen -full64 -h 帮助命令 总结 前言 ralgen是VCS工具自带的脚本,用于uvm中生成寄存器模型,本文主要介绍uvmgen脚本。首先,介绍在uvm中,利用ralgen脚本,生成...
  • UVM 寄存器模型的应用场景 在了解了寄存器模型的常规方法之后,我们需要考虑如何利用这些方法来检查寄存器、以及协助检查硬件设计逻辑和比对数据。 在软件实现硬件驱动和固件层时,也会实现类似寄存器模型镜像值的...
  • VCS的ralgen命令产生UVM寄存器模型

    千次阅读 2021-12-16 11:11:33
    文章目录前言一、寄存器描述表格二、生成 .ralf 寄存器文件2.1 field2.2 register2.3 register block2.4 system三、产生UVM寄存器模型 前言     在验证时,寄存器模型是必不可少的一个环境,且在项目中可能面临...
  • UVM中的寄存器模型

    千次阅读 2020-10-15 15:45:03
    寄存器模型简介 1. 通常来说,DUT中会有一组控制端口,通过控制端口,可以配置DUT中的寄存器,DUT可以根据寄存器的值来改变其行为。这组控制端口就是寄存器配置总线。 在没有寄存器模型之前,只能启动sequence通过...
  • 目录寄存器模型对DUT的模拟期望值与镜像值常用操作及其对期望值和镜像值的影响寄存器模型中一些内建的sequence*检查后门访问中hdl路径的sequence*检查默认值的sequence*检查读写功能的sequence 寄存器模型对DUT的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 84,350
精华内容 33,740
关键字:

寄存器模型