RAL 寄存器模型 --- 概述

本文介绍了在UVM验证环境中如何使用寄存器模型简化寄存器访问,包括寄存器检测方法、寄存器模型的作用(如解耦Scoreboard和Sequence、简化流程等)、Froutdoor和Backdoor的访问方式,以及RAL模型的层次结构,重点阐述了Adapter在模型中的关键作用和连接机制。
摘要由CSDN通过智能技术生成

目录

寄存器模型概述

1.为什么要使用寄存器模型?

1)寄存器检测的方法

2)寄存器模型的作用

3)Froutdoor

4)Backdoor

 2.RAL模型层次结构

1)uvm_reg_field

2)uvm_reg

3)uvm_reg_block

4)uvm_reg_map

3.Adapter


寄存器模型概述

1.为什么要使用寄存器模型?

在复杂的UVM验证环境中,访问DUT中的寄存器可能会涉及到多个模块和层级的交互。初学者可能会发现手动处理这些交互过于复杂。

使用UVM寄存器模型,验证工程师可以直接调用内置的函数来访问寄存器,而无需处理复杂的交互过程。

1)寄存器检测的方法

通常来说,DUT中会有一组控制端口,通过控制端口,可以配置DUT中的寄存器,DUT可以根据寄存器的值来改变其行为。这组控制端口就是寄存器配置总线。

a. 无寄存器模型:

启动读写的sequence进出完成,sequence产生的读写事物通过sequencer传入到对应的driver当中,driver讲对应的信号驱动给dut中,dut根据驱动的信号做出相应的动作,如改变寄存器的值或者返回寄存器的值,monitor再从接口上捕获到接口信息,可以判断drive操作的哪个寄存器以及对应的值,再讲信息送到sb中,进行对比。

这个方法很难在其他组件中在适当的时间启动读写寄存器。

ral提供了用于对设计当中的寄存器和存储器进行建模的register model,是一套数据结构,需要使用他们建立寄存器模型。

b.有寄存器模型:

有了寄存器模型之后,scoreboard只与寄存器模型打交道,无论是发送读的指令还是获取读操作的返回值,都可以由寄存器模型完成。有了寄存器模型后,可以在任何耗费时间的phase中使用寄存器模型以前门访问或后门(BACKDOOR)访问的方式来读取寄存器的值,同时还能在某些不耗费时间的phase(如check_phase)中使用后门访问的方式来读取寄存器的值。

前门访问与后门访问是两种寄存器的访问方式。所谓前门访问,指的是通过模拟cpu在总线上发出读指令,进行读写操作。在这个过程中,仿真时间($time函数得到的时间)是一直往前走的。

UVM寄存器模型的本质就是重新定义了验证平台与DUT的寄存器接口,使验证人员更好地组织及配置寄存器,简化流程、减少工作量。

2)寄存器模型的作用

  1. 解耦Scoreboard和Sequence

    有了寄存器模型,Scoreboard可以通过寄存器模型间接访问DUT中的寄存器和存储器,而不需要直接与Sequence紧密耦合。这种解耦使得Scoreboard的设计更加灵活,能够更好地适应复杂的验证环境。
  2. 简化寄存器访问流程

    使用寄存器模型提供的API,Scoreboard可以直接对寄存器和存储器进行访问,而不需要通过Sequence来启动访问操作。这简化了寄存器访问的流程,提高了验证环境的可维护性和可扩展性。
  3. 提供读写接口

    寄存器模型提供了read和write等接口,使得Scoreboard可以方便地读取寄存器的值或向指定的寄存器写入值。这种直接的读写接口使得Scoreboard能够更加灵活地与DUT进行通信和交互。
  4. 事务转换器的使用

    为了让寄存器模型能够与Sequence进行交互,通常需要使用事务转换器。这个转换器能够将寄存器模型产生的事务类型转换成Sequence能够接受的类型,从而实现寄存器模型与Sequence之间的无缝连接和通讯。
  5. 寄存器模型与Sequencer的连接

    通过事务转换器,寄存器模型和Sequencer得以连接。这样一来,寄存器模型就可以直接与DUT进行通信,而不需要依赖于Sequence的启动,从而进一步简化了验证流程和提高了效率。

3)Froutdoor

scoreboard或者是平台的其他组件当中,调用寄存器模型的相关API进行寄存器访问时,寄存器模型会根据这个API以及寄存器的名称自动产生出一个类型为uvm_reg_item的事物对象。

比如,如果是read操作,那么这个对象中的操作类型就是独操作,访问的地址会根据寄存器的名称,register name自动生成。接着,寄存器模型会将这个事物对象进一步进行转换,转换为uvm_reg_ bus_op类型的事物对象。并将事物对象传入adapter转换器,转换器再将其转换为sequencer可接受的事物类型。sequencer在收到后,通过driver驱动到相应的dut,最终返回相应的值。

下面是一个简单的伪代码示例,展示了如何使用寄存器模型进行寄存器的读取操作:

import uvm_pkg::*;

class scoreboard extends uvm_component;
    // 寄存器模型
    uvm_reg_model reg_model;

    // 构造函数
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    // 重写build_phase方法
    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);

        // 初始化寄存器模型
        reg_model = new("reg_model", this);
    endfunction

    // 读取寄存器值的方法
    virtual task read_register(string reg_name);
        // 创建一个uvm_reg_item类型的事务对象
        uvm_reg_item reg_item = new();
        // 设置事务对象的操作类型为读操作
        reg_item.operation = uvm_reg::UVM_READ;
        // 设置访问的寄存器名称
        reg_item.element = reg_model.get_reg_by_name(reg_name);

        // 将uvm_reg_item类型的事务对象转换为uvm_reg_bus_op类型
        uvm_reg_bus_op bus_op = reg_model.convert2bus(reg_item);

        // 将事务对象传递给适当的adapter转换器进行进一步处理
        // 这里省略了转换器的具体实现

        // 最终将事务对象传递给Sequencer并驱动到DUT中
        uvm_sequencer#(uvm_reg_bus_op) sequencer;
        sequencer.drive(bus_op);
    endtask
endclass

4)Backdoor

通过dut的层次结构直接进行引用,操作完成后寄存器模型再将结果返回sb。以下是一个简单的伪代码示例,演示了如何使用后门方式操作寄存器:

import uvm_pkg::*;

class scoreboard extends uvm_component;
    // 寄存器模型
    uvm_reg_model reg_model;

    // 构造函数
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    // 重写build_phase方法
    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);

        // 初始化寄存器模型
        reg_model = new("reg_model", this);
    endfunction

    // 后门写寄存器值的方法
    virtual task write_register_backdoor(string reg_name, bit [31:0] value);
        // 获取寄存器对象
        uvm_reg reg = reg_model.get_reg_by_name(reg_name);
        if(reg == null) begin
            `uvm_error("SCOREBOARD", $sformatf("Register %s not found!", reg_name));
            return;
        end

        // 使用后门方式写入寄存器值
        reg_model.do_write(reg, value);

        // 从寄存器模型中读取更新后的寄存器值
        bit [31:0] updated_value;
        reg_model.do_read(reg, updated_value);

        // 将更新后的寄存器值传递给 Scoreboard 进行后续处理
        handle_register_update(reg_name, updated_value);
    endtask

    // 后续处理更新后的寄存器值
    virtual task handle_register_update(string reg_name, bit [31:0] value);
        // 在这里进行 Scoreboard 对更新后的寄存器值的处理
        `uvm_info("SCOREBOARD", $sformatf("Register %s updated with value %h", reg_name, value), UVM_HIGH);
    endtask
endclass

 2.RAL模型层次结构

uvm_reg_field 表示寄存器中的一个字段,uvm_reg 表示一个完整的寄存器,uvm_reg_block 用于组织和管理寄存器,而 uvm_reg_map 则管理寄存器地址映射关系。这些组件共同构成了 UVM 寄存器模型中的层次结构。

 以下是层次结构图:

1)uvm_reg_field

这是寄存器模型中的最小单位。什么是 reg_field ?假如有一个状态寄存器,它各个位的含义如下图 所示。

如上的状态寄存器共有四个域,分别是 empty full overflow underflow 。这四个域对应寄存器模型中的 uvm_reg_field 。名字 为“reserved” 的并不是一个域。

2)uvm_reg

它比uvm_reg_field高一个级别,但是依然是比较小的单位。一个寄存器中至少包含一个uvm_reg_field
以下是uvm_reg相关的伪代码:
// 定义一个状态寄存器的 uvm_reg_field
class status_reg_field extends uvm_reg_field;
    function new(string name = "status_reg_field");
        super.new(name, 1, 0, "RO", 0);
    endfunction
endclass

// 定义一个状态寄存器的 uvm_reg
class status_reg extends uvm_reg;
    status_reg_field empty_field;
    status_reg_field full_field;
    status_reg_field overflow_field;
    status_reg_field underflow_field;

    function new(string name = "status_reg");
        super.new(name, 32, UVM_NO_COVERAGE);
        empty_field = new("empty", 0, 0);
        full_field = new("full", 1, 1);
        overflow_field = new("overflow", 2, 2);
        underflow_field = new("underflow", 3, 3);
    endfunction
endclass

3)uvm_reg_block

它是一个比较大的单位,在其中可以加入许多的uvm_reg,也可以加入其他的uvm_reg_block。一个寄存器模型中至少包含一个uvm_reg_block
以下是uvm_reg_block相关的伪代码:

// 定义一个 uvm_reg_block,包含一个状态寄存器
class my_reg_block extends uvm_reg_block;
    status_reg my_status_reg;

    function new(string name = "my_reg_block");
        super.new(name);
        my_status_reg = new("my_status_reg");
    endfunction
endclass

4)uvm_reg_map

每个寄存器在加入寄存器模型时都有其地址, uvm_reg_map 就是存储这些地址,并将其转换成可以访问的物理地址(因为加入寄存器模型中的寄存器地址一般都是偏移地址,而不是绝对地址)。当寄存器模型使用前门访问方式来实现读或写操作时,uvm_reg_map 就会将地址转换成绝对地址,启动一个读或写的 sequence ,并将读或写的结果返回。在每个reg_block内部,至少有一个(通常也只有一个)uvm_reg_map
以下是uvm_reg_map相关的伪代码:
// 定义一个 uvm_reg_map,用于管理寄存器地址映射
class my_reg_map extends uvm_reg_map;
    function new(string name = "my_reg_map");
        super.new(name);
        // 设置寄存器地址映射关系
        add_reg(my_reg_block.my_status_reg, 0x1000);
    endfunction
endclass


// 在每个 reg_block 内部,至少有一个 uvm_reg_map
// 创建一个 reg_block 实例,并添加到 uvm_reg_map 中
initial begin
    my_reg_block my_block = new();
    my_reg_map my_map = new();

    // 在 reg_block 内部添加 uvm_reg_map
    my_block.add_map(my_map, 0);
end

3.Adapter

在 UVM(Universal Verification Methodology)中,Adapter 通常用于将事务或数据从一个接口传递到另一个接口,并在传递过程中进行必要的格式转换或协议转换。

在寄存器模型中,Adapter 的主要作用是将前门(Frontdoor)访问请求从验证环境传递给 DUT(Design Under Test),或者将后门(Backdoor)访问请求从 DUT 传递给验证环境。它负责在验证环境和 DUT 之间建立通信桥梁,确保正确而高效地进行寄存器访问。

Adapter 可以在不同层次或不同接口之间进行数据格式的转换、地址的映射、时序的调整等操作,以确保寄存器访问的正确性和一致性。它通常由一系列方法和类组成,用于处理不同类型的访问请求,并将其转换为适合目标接口的格式。

总的来说,Adapter 在寄存器模型中扮演着连接验证环境和 DUT 的关键角色,它通过数据转换和传递实现了验证环境与 DUT 之间的通信和交互。

1)reg2bus

它的作用是将由寄存器模型产生的uvm_reg_bus_op类型的事物对象转换为我们自定义类型的事物。对象转换之后的事物对象会自动的发往sequencer当中。

class my_transaction extends uvm_sequence_item;
    // 用户自定义事务类型
    // 可以根据需要添加字段和方法
endclass

class reg2bus_adapter extends uvm_adapter;
    // 适配器,用于将寄存器模型产生的 uvm_reg_bus_op 转换为用户自定义事务类型

    virtual function uvm_sequence_item convert2bus(uvm_object obj);
        my_transaction trans = new();
        uvm_reg_bus_op bus_op = obj;
        // 在这里执行转换逻辑,将 bus_op 中的信息转换为 my_transaction 类型的事务对象
        // 示例中简单地将 bus_op 中的数据拷贝到 trans 中
        trans.address = bus_op.addr;
        trans.data = bus_op.data;
        return trans;
    endfunction
endclass

class my_env extends uvm_env;
    // 测试环境,用于接收转换后的事务对象并发送到 sequencer 中

    reg2bus_adapter adapter;

    function new(string name = "my_env", uvm_component parent = null);
        super.new(name, parent);
        adapter = new();
    endfunction

    virtual task handle_bus_op(uvm_reg_bus_op bus_op);
        my_transaction trans;
        // 使用适配器进行转换
        trans = adapter.convert2bus(bus_op);
        // 将转换后的事务对象发送到 sequencer 中
        seqr.put(trans);
    endtask
endclass

2)bus2reg

它的作用与reg2Bus刚好相反,是将我们自定义的事物类型的对象转化为uvm_reg_bus_op类型的事物。对象转换之后的事物对象会被寄存器模型所分析。之后更新寄存器模型中相关寄存器的值。

// 定义适配器,实现bus2reg的转换
class bus_to_reg_adapter extends uvm_adapter;
    // 重载unadapt方法,实现bus2reg的转换
    virtual function uvm_object unadapt(uvm_sequence_item seq_item);
        my_transaction seq_item_in = seq_item;
        // 实现bus2reg的转换逻辑,将用户自定义类型的事务对象转换为uvm_reg_bus_op类型的事务对象
        // ...
        return uvm_reg_bus_op::type_id::create("bus_to_reg_trans"); // 返回转换后的事务对象
    endfunction
endclass

3)register model 和adapter 以及 sequencer的连接:

实现这个连接的方式很巧妙,要借助于寄存器模型当中的map来完成。

在这个map当中,不但包含了各个寄存器以及存储器的地址映射,而且还包含了一个用于与adapt连接的adapt类型的句柄,连接的过程其实就是将env当中的adapt对象。复制给这个句柄的过程,同样的也包含了用于与sequencer连接的句柄连接的过程,也是将agent当中的sequencer对象复制给这个句柄的过程,此外还有一些任务,用于寄存器模型和平台协调。

class my_env extends uvm_env;
    reg_bus_adapter adapter; // 适配器
    my_sequencer seqr; // Sequencer

    function new(string name = "my_env", uvm_component parent = null);
        super.new(name, parent);
        adapter = new();
        seqr = my_sequencer::type_id::create("seqr", this);
    endfunction

    // 连接register model、adapter和sequencer
    virtual function void connect_register_model(uvm_reg_block reg_block);
        uvm_reg_map map;
        // 创建映射,并设置adapter和sequencer
        map = reg_block.create_map("reg_map", 0, UVM_BACKDOOR, 32'h0, 32'h1000);
        map.set_sequencer(seqr); // 设置映射的sequencer
        map.set_adapter(adapter); // 设置映射的adapter
    endfunction

    // 任务用于启动sequence,以访问DUT中的寄存器
    virtual task start_sequence();
        my_sequence seq;
        seq = my_sequence::type_id::create("seq");
        seq.start(seqr); // 启动sequence并传递sequencer
    endtask
endclass

在这个示例中,my_env 类中的 connect_register_model 方法用于连接寄存器模型、适配器和 sequencer。在该方法中,首先创建了一个寄存器映射(uvm_reg_map),然后设置了该映射的 adapter 和 sequencer。当寄存器模型需要访问寄存器时,会通过该映射将请求转发给 adapter,然后 adapter 再将请求转发给 sequencer 进行处理。

另外,start_sequence 任务用于启动 sequence,以访问 DUT 中的寄存器。在该任务中,创建了一个 sequence 对象,并调用其 start 方法启动 sequence,并传递 sequencer 给它,从而开始访问 DUT 中的寄存器。

  • 27
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 寄存器模型是一种设计硬件的方法,用于描述和模拟硬件系统中的寄存器及其行为。它通常使用HDL(硬件描述语言)编写,包括描述寄存器寄存器之间的逻辑关系。 ralgen是一个常用的生成寄存器访问层(RAL)的工具,可用于自动生成寄存器模型的代码。它可以根据给定的寄存器描述文件自动创建寄存器模块和对应的读写接口。 生成ralgen脚本的主要步骤如下: 1. 定义寄存器:首先根据硬件设计需求,定义所需的寄存器、位字段以及其相应的属性,如宽度、默认值等。 2. 构建寄存器模型:根据寄存器定义,使用ralgen语法构建寄存器模型,并定义寄存器模块的名称和接口。 3. 生成脚本:使用ralgen工具,根据寄存器模型文件生成对应的ralgen脚本,该脚本包含寄存器访问层的代码实现。 ralgen脚本生成完成后,可以在实际的硬件设计中使用。它提供了简单、高效的寄存器访问接口,以便于软件和测试团队对寄存器进行读写操作。同时,脚本还包含了对寄存器的各种访问控制和功能验证,提高了开发效率和设计的可靠性。 总之,通过使用寄存器模型ralgen脚本,设计人员可以更加方便地创建寄存器访问层,并提供了更好的可扩展性和重用性,加快了硬件设计的开发进程。 ### 回答2: 寄存器模型生成ralgen脚本是为了自动生成寄存器抽象层的代码,以提高设计效率和减少手工工作。它是基于寄存器抽象层(Register Abstraction Layer,RAL)的基础上进行的。 首先,我们需要创建一个RAL模型,定义模型中的寄存器及其属性。通过设置寄存器的名称、地址、位宽、访问权限等信息,我们可以对寄存器进行准确的描述。然后,根据需要,我们可以在寄存器中添加字段,用于描述寄存器中的各个位。这些字段可以设置位宽、访问权限、默认值等属性。 确定好寄存器模型后,我们可以使用ralgen工具来生成RAL代码。ralgen根据寄存器模型生成的脚本,可以自动生成寄存器抽象层的代码。该代码包括寄存器的读写接口、寄存器字段的操作接口等,并且根据所选的模板生成用户需要的代码语言,如C++、SystemVerilog等。 通过使用ralgen工具生成的RAL代码,我们可以方便地进行寄存器的配置和操作。RAL抽象层提供了一种高层次的接口,使得寄存器操作更简洁、更易于维护。同时,它还提供了寄存器的访问权限管理、寄存器模式的验证等功能,有助于确保设计正确性。 总结起来,寄存器模型生成ralgen脚本是一种自动化的方法,可以根据寄存器模型快速生成寄存器抽象层的代码。这样做可以提高设计效率,减少手工工作,同时也有助于提高设计的可靠性和可维护性。 ### 回答3: 寄存器模型生成ralgen脚本是一种用于自动生成寄存器抽象层代码的方法。ralgen脚本是使用ralgen工具进行寄存器模型生成的输入脚本。 使用寄存器模型可以在硬件设计中对寄存器进行更高层次的抽象,使得设计工作更加高效和可靠。通过ralgen脚本,可以快速生成寄存器抽象层代码,从而减少了手动编写的工作量,并且避免了人为错误。 ralgen脚本的编写通常包括以下步骤: 1. 确定寄存器的名称、地址、位宽等信息。 2. 根据寄存器的功能和特性,选择合适的寄存器字段,如读写控制、数据位、校验位等。 3. 对于每个寄存器字段,指定它的属性和寄存器的访问权限,如读或写。 4. 根据需要,可以指定寄存器的默认值、重置值等。 5. 定义寄存器集,将相关的寄存器归类到不同的寄存器集中。 编写ralgen脚本时需要注意以下几点: 1. 脚本的语法应符合ralgen的规范,可以参考ralgen工具的文档和示例。 2. 寄存器字段的定义应准确、清晰,以便于后续的寄存器模型使用和生成。 3. 在编写脚本时,可以根据具体的需求和设计要求,增加一些额外的功能和选项,如寄存器的自动重置、清零等。 通过这样的方式,使用寄存器模型生成ralgen脚本可以帮助我们实现更高效和可靠的寄存器抽象层代码生成,从而提高硬件设计的开发效率和质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值