if(m_gnt[i])begin//lowestindexhaspriority
addr_a=m_addr[i];
wdata_a=m_wdata[i];
RE_a=m_RE[i];
WE_a=m_WE[i];
break;
end
end
end
时序逻辑
寄存器(触发器),一个综合的Verilog设计通常被定义为一个aways块,是敏感的时钟边缘(可能加一个异步复位的reset)。
SystemVerilog增加了的always_ff建设表示时序逻辑的描述过程。
合成工具是必需的检查always_ff过程真的时序逻辑(只有一个定时控制,不堵定时控制语句的变量没有赋值,也将被写入其他进程)。
这是通常使用非阻塞赋值到变量内的always_ffprocess,以避免模拟多个进程所引发的相同的时钟边缘(只是因为它是时钟的always过程中的Verilog)之间的竞争条件。
下面的示例演示计数器模块的过程。
注意,计数器的值没有被写入count++,因为这被视为一个阻塞分配和某些合成工具不允许阻塞和非阻塞的分配到相同的一个进程内的变量。
always_ff@(posedgeclkorposedgereset)
begin
if(reset==1'b1)
count<=0;
else
if(enable==1'b1)count<=count+1'b1;
end
Memories(存储器)
具有多层面的数组可以声明SystemVerilog中。
这些可以有包装尺寸(尺寸在变量名前指定),无包装的尺寸(尺寸在变量名后指定)。
一个二维阵列的逻辑与一个包装尺寸和一个解压缩尺寸可以被合成为一个嵌入式RAM块,如果在其控制的处理相一致的实际RAM阵列被访问。
下面的例子是一个完整的模块,设置模块参数的大小,合成同步RAM。
moduleslave#(parameterasize=8)(interfacebus);
importtypes:
:
*;
wdata_t[(2**asize)-1:
0]memory;
always_ff@(posedgebus.clk)begin
if(bus.WE)
memory[bus.addr]<=bus.wdata;
if(bus.RE)
bus.rdata<=memory[bus.addr];
end
endmodule:
slave
有限状态机
许多FPGA设计包含有限状态机(FSM)。
SystemVerilog中的这些综合的代码编写更灵活,比Verilog中的枚举类型可以用来表示一组的状态。
当一个枚举类型,用于状态寄存器,编译器将检查只有合法的枚举值被分配到状态寄存器。
默认情况下,FPGA综合工具,将选择一个合适的编码方式来表示每个枚举值。
在许多情况下,这将是一热编码在FPGA(实际使用的编码可以通过设置选项中的合成工具控制),以产生最有效的执行。
一个有限状态机编写代码的几种风格的流行(例如,一个单一的主频过程或一颗主频过程加上一个组合的过程)。
以下示例显示了一个单时钟进程FSM在我们的总线主站模块的一部分。
typedefenumlogic[5:
0]{IDLE,REQ,WAITING,READ0,READ1,WRITE0}bus_trans_t;
bus_trans_tbus_trans_state;
always_ff@(posedgebus.clkorposedgebus.reset)begin
//asynchronousreset
if(bus.reset)begin
acount=0;
bus.req<=0;
bus_trans_state<=IDLE;
bus.WE<=0;
bus.RE<=0;
bus.addr<=0;
bus.wdata<=0;
end
elsebegin
case(bus_trans_state)
IDLE:
begin
bus_trans_state<=REQ;
bus.WE<=0;
bus.RE<=0;
end
REQ:
begin
bus.req<=1;
bus_trans_state<=WAITING;
end
WAITING:
begin
if(bus.gnt)begin
acount++;
bus_trans_state<=READ0;
bus.WE<=0;
bus.RE<=1;
bus.addr<={1'b0,acount};
end
end
READ0:
begin
bus_trans_state<=READ1;
bus.WE<=0;
bus.RE<=0;
end
READ1:
begin
//setdatastructfromrdatavector
data=bus.rdata;
//manipulatedatastructmembers
data.data_h={data.data_h[2:
0],data.data_h[3]};
data.data_l={data.data_l[2:
0],data.data_l[3]};
bus_trans_state<=WRITE0;
bus.WE<=1;
bus.RE<=0;
bus.addr<={1'b1,acount};
bus.wdata<=data;
end
WRITE0:
begin
bus.req<=0;//clearrequest
bus_trans_state<=IDLE;
bus.WE<=0;
bus.RE<=0;
end
endcase
end
end
Hierarchy层次
在SystemVerilog的设计创建一个层次结构较Verilog简单主要有两个原因。
第一个原因是,SystemVerilog中,您可以使用变量来连接模块实例的端口,你不必申报线。
这意味着,FPGA设计人员可以停止担心什么时候应使用导线什么时候应使用变量。
一般的规则是,只使用使用变量(除了一些特殊情况下,如三态双向引脚)。
第二个原因是隐式的。
名端口连接。
这些允许的信号连接到指定的端口被省略的.name端口,如果它的名字是完全一样的端口名称(例如,如果端口和信号都CLK)。
它甚至可以使用通配符(.*),而不是端口名称,这意味着每个端口连接的信号具有相同的名称作为端口。
然而,通配符端口连接不喜欢的一些设计师,因为它是很难跟踪在层次结构中,如果端口连接的端口名称中未列出的模块实例。
下面的代码显示了最高级别的基于总线的设计,包括多种隐式和显式的端口connections.The主人和奴隶被连接到interfaceports的。
接口在下一节中解释。
moduletop(inputlogicclk,reset,enable,
outputlogicRE,
outputlogicWE,
outputtypes:
:
addr_taddr,
outputtypes:
:
rdata_trdata,
outputtypes:
:
wdata_twdata);
logicen_data_gen,en_data_gen_b;
intf#(.nmasters
(2),.nslaves
(2))bus(.clk,.reset);
data_genm1(.enable(en_data_gen_b),.bus(bus.master[0].mport));
masterm0(.bus(bus.master[1].mport));
slave#(.asize(7))s0(.bus(bus.slave[0].mport));
slave#(.asize(7))s1(.bus(bus.slave[1].mport));
counterc0(.clk,.reset,.enable,.q(en_data_gen));
assignaddr=bus.addr_a;
assignrdata=bus.rdata_a;
assignwdata=bus.wdata_a;
assignRE=bus.RE_a;
assignWE=bus.WE_a;
assignen_data_gen_b=!
en_data_gen;
endmodule:
top
总线接口和总线架构逻辑
使用SystemVerilogFPGA合成最有吸引力的原因之一是它的代表在一个易于管理的块被称为一个interface与片上总线的复杂的连接和逻辑能力。
不像大多数到目前为止,讨论的主题,这并没有任何同等结构VHDL(2008年版)。
一个SystemVerilog的接口中的一个模块,因为它可以包含端口和过程是相似的,但不同的是模块,它可以连接到一个端口。
代表所有的电线内的片上总线的接口,因此只需要一个端口连接到总线上的每个主机和从机。
此外,modports在接口允许主端口和从机端口有不同的特点。
下面的代码段显示的要点简单的例子,在我们的系统中,表示该仲裁总线的接口。
第一部分显示了需要的时钟和复位总线逻辑接口的端口。
它还显示了如何使用参数的设置大小,内部的信号连接到总线上的主机和从机的数量来体现。
interfaceintf#(parameterintnmasters=2,nslaves=1)
(inputlogicclk,reset);
importtypes:
:
*;
localparammvec_size=$clog2(nmasters);
localparamsvec_size=$clog2(nslaves);
//arbitratedsignals
addr_taddr_a;
wdata_twdata_a;
rdata_trdata_a;
logicRE_a;
logicWE_a;
//signalsto/fromeachmaster
addr_t[mvec_size:
0]m_addr;
wdata_t[mvec_size:
0]m_wdata;
logic[mvec_size:
0]m_req;
logic[mvec_size:
0]m_gnt;
logic[mvec_size:
0]m_RE;
logic[mvec_size:
0]m_WE;
//signalsto/fromeachslave
rdata_t[svec_size:
0]s_rdata;
logic[svec_size:
0]s_RE;
logic[svec_size:
0]s_WE;
接下来,modports需要被定义为将要创建的每个主机和从机。
在第二部分中,我们已经使用了generate语句,使用的nslavesand的nmasters参数来创建正确数量的modports。
在写作的时候,并不是所有的综合工具,支持在一个界面中使用的生成。
使用生成的另一种方法是创建一个接口,与内部信号和modports,支持的最大数量的主机和从机预计。
将被删除任何未连接的信号和modports的优化器自动合成过程中。
在理想的情况下,每个主站和从站被连接到总线的总线(例如,与每个连接的复用的信号相关联的元素)的内部结构的不应该知道。
每主人和奴隶modport的(记得生成将创建数组的modports)可以得到一组通用的端口名称使用“modport表达式”,例如主机可以访问一个名为reqin其modport端口内部连接的接口信号m_req[0]或m_req{1]等
generate
genvarm,s;
for(s=0;sslave
modportmport(inputclk,reset,
input.addr(addr_a[($left(addr_a)-$clog2(nslaves)):
0]),
output.rdata(s_rdata[s]),
input.wdata(wdata_a),
input.RE(s_RE[s]),
input.WE(s_WE[s]));
end
for(m=0;mmaster
modportmport(inputclk,reset,
output.req(m_req[m]),
input.gnt(m_gnt[m]),
output.addr(m_addr[m]),
input.rdata(rdata_a),
output.wdata(m_wdata[m]),
output.RE(m_RE[m]),
output.WE(m_WE[m]));
end
endgenerate
几个进程在我们的接口中指定的仲裁,地址解码和复用总线逻辑主机和从机输出。
下面的过程,在这个例子中定义了简单的仲裁:
它设置了一个位中的m_gnt的寄存器,对应于主已被授予访问总线(主指数最低,有一个请求等待)。
代码复用的主输出的过程已经表明:
地址译码器的逻辑过程是类似的。
always@(posedgeclkorposedgereset)begin
if(reset)begin
m_gnt<=0;
end
elsebegin
if(m_gnt)begin//gntpersistsuntilmasterdropsreq
for(inti=0;iif(m_gnt[i])m_gnt[i]<=m_req[i];
end
elsebegin//nomastercurrentlyhasgnt
for(inti=0;iif(m_req[i])begin//masterihaspriority
m_gnt[i]<=1;
break;
end
end
end
end
end
结论
如果您正在使用VerilogFPGA设计,有明显的优势,在更新您的设计流程,而不是使用SystemVerilog(它仍然是向后兼容所有现有的Verilog代码)。
如果你是刚刚起步,它是有道理的,选择的SystemVerilog作为你的第一语言学习FGPA设计。
如果您正在使用VHDL你可能想知道,如果在切换到SystemVerilog的努力可能是有道理的吗?
如果您的FPGA还包含片上复用总线与多个主机和从机,它可能是!
一旦你知道了使用SystemVerilog设计的基本知识,你可能会想看看在测试平台的SystemVerilog语言的功能。
SystemVerilog是一个“大”语言的结构中列出的LRM支持先进的验证方法,使用的技术,如约束随机生成的测试向量,功能覆盖到哪些方面经过全面测试您的设计,断言来发现设计错误,在这方面,SystemVerilog是VHDL或Verilog强大得多,但是那是另一个故事!
本例中使用的SystemVerilog的功能都包含在深入ourSystemVerilog为设计组andComprehensive的SystemVerilog的课程(连同许多其他功能,这里就不讨论了)。