SV学习之interface

普通的模块使用法:注意我们这里只实现了部分功能。。。。不是完全的读写模块。。。。

module mem_core(

input logic wen,

input logic ren,

output logic mrdy=1,

input logic [7:0] addr,

input logic [7:0] mem_din,  //写进mem

output logic [7:0] mem_dout,   //从mem读出

output logic status,

input logic clk);

logic[7:0] mem [7:0];   //初始化一个mem

initial $readmemh("d:/init.txt",mem);    //d:/init.txt 文件中是 @01  10  。

//或者   assign mem [2‘h01]=8‘b00000111;     注意这里一定要用 initial 或者assign等语句,不能直接=

task reply_read(input logic [7:0] data, integer delay);

#delay;

@(negedge clk)

mrdy=1‘b0;

mem_dout=data;   //从图中可看出这两句话几乎同时执行。

@(negedge clk)

mrdy=1‘b1;

endtask

[email protected](negedge ren) reply_read(mem[addr],10);

endmodule

module cpu_core(

output logic wen=1,

output logic ren=1,

input logic mrdy,

output logic [7:0] addr=0,

input logic [7:0] cpu_din,

output logic [7:0] cpu_dout,

output logic status=0,

input logic clk);

task read_memory(input logic [7:0] raddr, output logic [7:0] data);

@(posedge clk);

ren=1‘b0;

addr=raddr;

@(negedge mrdy);

@(posedge clk);

data=cpu_din;

ren=1‘b1;

endtask

initial begin

logic[7:0] read_data;

read_memory(2‘h01, read_data);

$display("Read Result", $time,read_data);

end

endmodule

module top;

logic mrdy,wen,ren;

logic[7:0] addr,d1,d2;

wor status;

logic clk=0;

mem_core mem(.*, .mem_din(d1), .mem_dout(d2));  //采用*对同名的信号做默认连接

cpu_core cpu(.*, .cpu_din(d2), .cpu_dout(d1));

initial for(int i=0;i<=255;i++) #1 clk=!clk;

endmodule

另外,SystemVerilog引入一个重要的数据类型:interface。其主要作用有两个:一是简化模块之间的连接;二是实现类和模块之间的通信;

  • 随着复杂度的提高,模块间互联变得复杂,SV引入接口,代表一捆连线的结构,具有智能同步和连接功能的代码;

接口(interface)为硬件模块的端口提供了一个标准化的封装方式。

用interface来封装接口的信号和功能。interface的定

义是独立于模块的,通过关键字interface和endinterface包起来。此外,interface里面还可以

带时钟、断言、方法等定义。

一个interface 也可以有input,output或是inout端口。当interface例化时,只有当变量或是线网声明在一个interface的端口列表中才能通过名字或是位置来互连.

一种新加的和interface有关系的构造体是Modport 。它提供了module的interface端口和在特定的module中控制task和function使用的方向性信息。这些端口的方向可以在module中可以看到。接口使用无信号的连接方式。Modport将接口中信号分组并指定方向。就像下图中的黑色矩形块里面一样,黑盒,我们从外面看并不关心Modport的定义,只需要考虑clk。

interface membus(input logic clk, output wor status);

logic mrdy;

logic wen;

logic ren;

logic [7:0] addr;

logic [7:0] c2m_data;

logic [7:0] m2c_data;

task reply_read(input logic [7:0] data, integer delay);

#delay;

@(negedge clk)

mrdy=1‘b0;

m2c_data=data;

@(negedge clk)

mrdy=1‘b1;

endtask

//Task和function可以定义在interface中,从而允许构造更抽象级的模型

task read_memory(input logic [7:0] raddr, output logic [7:0] data);

@(posedge clk);

ren=1‘b0;

addr=raddr;

@(negedge mrdy);

@(posedge clk);

data=m2c_data;

ren=1‘b1;

endtask

modport master(output wen, ren, addr, c2m_data, input mrdy, m2c_data, status, read_memory);

modport slave(input wen, ren, addr, c2m_data, output mrdy, m2c_data, status, reply_read);

//控制task和function使用的方向性信息,以便在下面的module中使用

endinterface

module mem_core(membus.slave mb);

//modport只需在模块首部指明(或者在()中),在模块例化时不需要指明使用接口时在模块和程序块之外声明接口变量;

//接口信号必须采用非阻塞值赋值来驱动。

logic[7:0] mem [7:0];

assign mem [2‘h01]=8‘b00000111;

assign mb.status=0;

[email protected](negedge mb.ren) mb.reply_read(mem[mb.addr],100);    //module可使用interface端口

endmodule

module cpu_core(membus.master mb);

assign mb.status=0;

initial begin

logic[7:0] read_data;

mb.read_memory(2‘h01, read_data);

$display("Read Result", $time,read_data);

end

endmodule

module top;

wor status;

logic clk=0;

membus mb(clk,status);

mem_core mem(.mb(mb.slave));

cpu_core cpu(.mb(mb.master));

initial for(int i=0;i<=255;i++) #1 clk=!clk;

endmodule

System verilog把测试平台的代码放在一个程序块中,包含代码和变量,

我总结了几步使用interface的方法

1、 首先定义一个interface

interface arb_if(input bit clk);

logic [1:0] grant, request;

logic reset;

clocking cb @(posedge clk);

//在其中定义一个时钟块。供下面的测试program使用。测试program中所有使用到的信号都应该定义在其中

output request;          //注意这里的方向是测试program中所需要的方向,一般跟DUT 中的相反

input grant;

endclocking

modport TEST (clocking cb,                             //  使用modport,将信号分组

output reset);

modport DUT (input request, reset, clk,

output grant);

modport MONITOR (input request, grant, reset, clk);

endinterface

2、定义一个基于interface参数的设计模块module

module arb (arb_if.DUT arbif);          //该interface参数要实例化

reg last_winner;

reg winner;

reg [1:0] next_grant;

reg [1:0] state, nxState;

always @(posedge arbif.clk or posedge arbif.reset)

begin

。。。

end

endmodule

3、定义一个基于interface参数的测试程序program

program automatic test (arb_if.TEST arbif);      //该interface参数也要实例化

task reset_test();

begin

$display("Task reset_test: asserting and checking reset");

arbif.reset <= 0;

#100 arbif.reset <= 1;   //测试program中所有使用到的信号都应该调用在interface中的时钟块里定义的信号

arbif.cb.request <= 0;

repeat (2) @arbif.cb;

arbif.reset <= 0;

@arbif.cb;                   //测试program中是这样等待时钟边沿的。

a0: assert (arbif.cb.grant == 2‘b00);

。。。

end

endtask

task request_grant_test();

begin

。。。

end

endtask

//注意program中不允许使用always块。

initial begin

repeat (10) @arbif.cb;

reset_test();

request_grant_test();

repeat (10) @arbif.cb;

$finish;

end

endprogram

4、‘最后使用一个顶层模块将它们组合起来

module top;

bit  clk;

always #5 clk = !clk;

arb_if arbif(clk);      //实例化一个interface

arb a1 (arbif);         //实例化一个module,参数调用上面实例化的interface

test t1(arbif);           //实例化一个测试program,参数调用上面实例化的interface

endmodule

当然也可以隐式端口连接,值使用.*即可。

module top;

bit  clk;

always #5 clk = !clk;

arb_if arbif(.*);

arb a1 (.*);

test t1(.*);

endmodule

虚接口:虚接口是物理接口的句柄

interface 和 module是一样的, 都是静态的变量, 也就是在程序开始时, 内存中就有了其实例.

但是在class里使用virtual interface时之前有两部必须提前完成:

l 定义是将接口作为一个类进行定义。

l 实例化:在RTL级的顶层中对接口进行实例化。

先定义一个接口。

interface Rx_if (input logic clk);

logic [7:0] data;

logic soc, en, clav, rclk;

clocking cb @(posedge clk);

output data, soc, clav;

input  en;

endclocking : cb

modport DUT (output en, rclk,

input  data, soc, clav);

modport TB (clocking cb);

endinterface : Rx_if

例如网络交换机中DUT 的每一个通道都有一个接口。,一个Driver类可能会连接到很多接口。

我们可以在Driver类中使用一个虚接口作为参数。

class Driver;

virtual Rx_if.TB Rx;

//想一想,如果不是虚接口,而是一个普通接口,就像一个普通模块一样,是一个静态变量。比如我们在顶层模块例化了这个接口 Rx, 那么下面所有的 实例化的  drv[i]都是对这同一个接口 Rx进行操作,这显然不是我们想要的。

如果定义了virtual,则每个实例独立。

...

...

endclass

然后在测试program中 创建一组虚接口

program automatic test(Rx_if.TB Rx[4],

Tx_if.TB Tx[4],

output logic rst);

........

Driver drv[4];        //实例化了4个   Driver 对象,每个 Driver对象带有1个实例化的虚接口

.........

initial begin

virtual Rx_if.TB vRx_t=Rx;

//创建一组虚接口,由于这里定义了virtual,所以实例化的时候可以有Rx[].

for (int i=0; i<4; i++) begin

drv[i] = new(...., vRx[i]);

end

rst <= 1;

repeat (10) @Rx[0].cb;

rst <= 0;

for (int i=0; i<4; i++) begin

drv[i].run(5, driver_done);          //发送

.......

end

..........

endprogram : test

最后在顶层:

module top;

logic clk, rst;

Rx_if Rx[4] (clk);

,,,,

atm_router a1 (Rx[0], Rx[1], Rx[2], Rx[3], Tx[0], Tx[1], Tx[2], Tx[3], clk, rst);

test       t1 (Rx, Tx, rst);

initial begin

clk = 0;

forever #20 clk = !clk;

end

endmodule : top

定义一个interface,且实例化多个后,如果没有定义virtual,则在任何一个实例中修改了某个信号值,在其他实例中都会受到影响。如果定义了virtual,则每个实例独立。如果该interface只有一个实例,可用可不用virtual,有多个实例,需要virtual。

再举个例子:8位计数器

`timescale 1ns/1ns

interface X_if (input logic clk);

logic [7:0] din, dout;

logic reset_l, load;

clocking cb @(posedge clk);

output din, load;

input dout;

endclocking

always @cb             //接口里面也可以带子程序,断言,initial,always块等代码。

$strobe("@ : %m: dout= , din= , load= , reset= ",

$time, dout, din, load, reset_l);

modport DUT (input clk, din, reset_l, load,

output dout);

modport TB (clocking cb, output reset_l);

endinterface

// Simple 8-bit counter with load and active-low reset

`timescale 1ns/1ns

module DUT(X_if.DUT xi);

logic [7:0] count;

assign xi.dout = count;   //们想要输出的结果就是计数器

always @(posedge xi.clk or negedge xi.reset_l)

begin

if (!xi.reset_l)  count = 0;

else if (xi.load) count = xi.din;

else              count++;

end

endmodule

////////////////////////////////

`timescale 1ns/1ns

program automatic test();

parameter NUM_XI = 2;  // Number of interface instances

typedef virtual X_if.TB vXi_t;

vXi_t vxi[NUM_XI];          //虚接口数组

class Driver;        //在测试程序中定义类

vXi_t xi;

int id;

function new(vXi_t xi, int id);

this.xi = xi;

this.id = id;

endfunction

task reset;

fork

begin

$display("@ : %m: Start reset [ ]", $time, id);

// Reset the device

xi.reset_l <= 1;

xi.cb.load <= 0;

xi.cb.din <= 0;

@(xi.cb)

xi.reset_l <= 0;

@(xi.cb)

xi.reset_l <= 1;

$display("@ : %m: End reset [ ]", $time, id);

end

join_none

endtask

task load;

fork

begin

$display("@ : %m: Start load [ ]", $time, id);

xi.cb.load <= 1;

xi.cb.din <= id + 10;

xi.cb.load <= 0;

repeat (5) @(xi.cb);

$display("@ : %m: End load [ ]", $time, id);

end

join_none

endtask

endclass

Driver driver[];

initial begin

// Connect the local virtual interfaces to the top

$display("Test.v: There are NUM_XI = interfaces", NUM_XI);

if (NUM_XI <= 0) $finish;

driver = new[NUM_XI];            //创建driver,   每个DUT 要对应一个driver

vxi = top.xi;

//XMR跨模块连接。这种是推荐做法,就不用带参数了program automatic test(X_if xi[NUM_XI]); 了。

//注意这里其实是把top模块中生成的xi[]数组的句柄传过来的

for (int i=0; i《NUM_XI; i++)

begin

driver[i] = new(vxi[i], i);

driver[i].reset;

end

foreach (driver[i])

driver[i].load;

repeat (10) @(vxi[0].cb);

$display("@ : Test completed", $time);

$finish;

end

endprogram

////////////////////////////////////////////////////////

`timescale 1ns/1ns

parameter NUM_XI = 2;  // Number of interface instances

module top;

// Clock generator

bit clk;

initial forever #20 clk = !clk;

X_if xi [NUM_XI] (clk);  // Instantiate N Xi interfaces

// Generate N DUT instances

generate

for (genvar i=0; i《NUM_XI; i++)

begin : dut

DUT d (xi[i]);

end

endgenerate

// Instantiate the testbench, overriding the parameter with number of instances

test tb();

endmodule : top

时间: 2024-10-04 08:15:44

SV学习之interface的相关文章

Avalon总线学习 ---Avalon Interface Specifications

Avalon总线学习 ---Avalon Interface Specifications 1.Avalon Interfaces in a System and Nios II Processor 2.Avalon Interfaces in a System Design and External Processor 3.传统的读和写时序图 4.在slave端,读和写,被主设备设置了wait信号 5.Piplined 读数据,Latency可调 6. 7 8. 9. 10 11 12 13

SV中的Interface和Program

Interface:SV中新定义的接口方式,用来简化接口连接,使用时注意在module或program之外定义interface,然后通过'include来添加进工程. interface  arb_if(input bit clk);              //clk信号,一般单独拿出来 logic [1:0]grant, request;       //只定义信号类型.类型在不同的modport中分别定义. logic rst; clocking cb @(posedge clk); 

Java学习——接口Interface

接口: 初期理解可以认为是一个特殊的抽象类 当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示.class用于定义类interface 用于定义接口 接口定义时,格式特点:1,接口中常量见定义:常量,抽象方法.2,接口中的成员都有固定修饰符号 常量:public static final 方法:public abstracts 记住: 接口中的成员都是public的 接口:是不可以创建对象的,因为有抽象方法.需要被子类实现,子类对接口中的抽象方法全都覆盖后,子类才可以实例化.否则子类是

golang学习之interface与其它类型转换

如下函数,将interface变量in转换为int: func formatTimeStamp(in interface{}, layout string) (out string) { timeStr := in.(int) month, err := time.Parse(layout, string(timeStr)) if err != nil { return time.Now().Format(layout) } return month.Format(layout) } 查看in具

C# interface

我们学习了interface,即接口,其与抽象类有点像,但是他们也有一些区别,比如类不能多重继承但是接口却可以多重继承. 接口只包含方法.委托或事件和属性的签名(接口包含的成员).不能包含字段(因为字段是包含数据的).方法的实现是“继承”接口的类中完成的:接口可以包含的成员的访问修饰符只能且默认为public: 一个接口可以从一个或多个基接口继承: 接口类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员: 当基类型列表包含基类和接口时,基类必须是列表中的第一项: 实现接口的类可以显

iOS Cookie学习(NSHTTPCookieStorage的使用)

最近也是忙里偷闲,无意中发现了一位大牛的文章,小小的研究了一下HTTP.今天先写一点Cookie,之前有过文章都是关于Cookie的,也没有深入研究,今天也算是一起学习吧. @interface FQWebGlobalCookies : NSObject /* 全局单例 */ + (id)globalCookiesManager; /* 对每份URL产生的Cookie 保存起来,以便管理 对响应头域中的cookie进行保存 */ - (void)storeResponseCookies:(NSD

Dubbox学习笔记

Dubbox 学习笔记,本文观点仅代表个人理解,如有错误之处,请留言指正,谢谢. 本文主要根据Dubbox用户手册进行学习,从提供的配置去看dubbox的全貌,没有从源码角度去分析,而是从应用的角度上去学习. 1.dubbo配置学习 1)  dubbo:protocol   服务提供方协议配置,如果需要支持多协议,我可以声明多个 dubbo:protocol标签,并在 dubbo:service标签的protocol属性中指定. dubbo:protocol标签配置项如下: id name po

CBCentralManager学习笔记

// CBCentralManager学习笔记 @interface CBCentralManager : NSObject //CBCentralManager的几种状态 typedef NS_ENUM(NSInteger, CBCentralManagerState) { // 初始的时候是未知的(刚刚创建的时候) CBCentralManagerStateUnknown = 0, //正在重置状态 CBCentralManagerStateResetting, //设备不支持的状态 CBC

iOS Human Interface Guidelines(原创翻译)第三章

适配和布局 ·适配创建 通常来说,人们想要在他们所有的设备上使用他们最喜爱的app,不论是在设备什么样的定向上.在iOS8和稍后的版本中,你可以使用尺寸类和自动布局在设备放置环境改变时候通过定义屏幕布局,视图控制器和视图应当如何调整来帮助你满足这个期望.放置环境的概念可能会涉及到整个屏幕或者仅仅是屏幕的一部分,比如一个弹出菜单的区域或者是在一个分离的视图控制器中的主要视图. iOS定义了两种尺寸类别:常规和紧凑.常规尺寸类和宽松的空间联系到一起,紧凑尺寸类和有限的空间联系到一起.为了描述一种放置