SystemVerilog for design
2025-08-07
SystemVerilog硬件设计及建模
一、SystemVerilog中的变量
1.1 变量类型介绍
logic——四态变量,类似于reg;可以声明为任意大小的向量
enum——一组由符号表示的值得枚举型net或变量
typedef——一种用户定义的数据类型,由内建类型或其它用户定义类型构成
struct——一种可单独或同时引用的变量得集合体,类似于C语言得struct
二、SystemVerilog声明的位置
2.1包(package)
2.1.1 包的定义
// 类似与module与endmodule
// 为了使多个模块共享用户定义类型的定义
package definitions;
parameter VERSION="1.1"; //声明一个常数
typedef enum {ADD,SUB,MUL} opcodes_t; //自定义一个变量类型opcodes_t,后面可以用opcodes_t声明变量,这个变量可以取ADD,SUB,MUL三个值
typedef struct {
logic [31:0] a ;
logic [31:0] b ;
opcodes_t opcode ;
} instructions_t; //自定义一个变量类型instructions_t,
function automatic [31:0] multiplier(
input [31:0] a,
input [31:0] b
);
//用户定义的32位乘法代码从这开始
return a*b;//抽象的乘法器(无错误检测)
endfunction //定一个函数,输入两个32位数,返回它们相乘的结果,并截取低32位
endpackage
包中可以包含的可综合的结构有
parameter
和localparam
常量定义const
变量定义typedef
用户定义类型全自动
task
和function
定义包中task和function必须位automatic
从其他包中
import
语句操作符重载定义
2.1.2 包的用法
显式引用包中内容可读性高,但是麻烦
module ALU(
input definitions::instruction_t IW ,
input logic clock ,
output logic [31:0] result ,
);
always_ff @(posedge clock)begin
case(IW.opcode)
definitions::ADD:begin
result = IW.a + IW.b;
end
definitions::SUB:begin
result = IW.a - IW.b;
end
definitions::MUL:begin
result = definitions::multiplier(IW.a,IW.b);
end
endcase
end
endmodule
导入包中特定子项
module ALU(
input definitions::instruction_t IW ,
input logic clock ,
output logic [31:0] result ,
);
import definitions::ADD;
import definitions::SUB;
import definitions::MUL;
import definitions::multiplier;
always_comb begin
case(IW.opcode)
ADD:begin
result = IW.a + IW.b;
end
SUB:begin
result = IW.a - IW.b;
end
MUL:begin
result = definitions::multiplier(IW.a,IW.b);
end
endcase
end
使用包通配符导入
module ALU(
input definitons::instruction_t IW ,
input logic clock,
output logic [31:0] result
);
import definitions::*; //通配符导入
always_comb begin
case(IW.opcode)
ADD:begin
result = IW.a + IW.b;
end
SUB:begin
result = IW.a - IW.b;
end
MUL:begin
result = multiplier(IW.a,IW.b);
end
endcase
end
endmodule
2.2 编译单元$unit
2.2.1 编译单元定义
编译单元是同时编译的所有源文件。可以包含:
时间单位和精度声明
变量声明
net声明
常量声明
用户定义数据类型,使用
typedef
、enum
或class
任务和函数定义
/********************************外部声明********************************/
parameter VERSION="1.2a"; //外部变量
reg rst_n = 1; //外部变量(低有效)
typedef struct packed { //外部用户定义类型
reg [31:0] address ;
reg [31:0] data ;
reg [31:0] opcode ;
} instruction_word_t;
function automatic int log2(input int n); //外部函数
if(n<1)begin
return(1);
end
else begin
log2 = 0;
while(n>1)begin
n = n / 2;
log2++;
end
return(log2)
end
endfunction
/********************************模块定义********************************/
//用外部声明定义端口类型
module register (
input instruction_word_t d ,
input wire clock ,
output instruction_word_t q
);
always_ff @( posedge clock or negedge rst_n ) begin
if(!rst_n)begin
q <= 1'b0;
end
else begin
q <= d;
end
end
endmodule
2.2.2 编码指导
不要在$unit域中进行任何声明!声明在命名包内进行
$unit域可以导入对应的包
2.2.3 源代码顺序
module parity_gen (
input wire [63:0] data
);
assign parity = ^data; //parity是一个隐式局部net
endmodule
reg parity; //因为声明在被perity_gen引用之后出现
//因此外部声明没被perity_gen使用
module parity_check (
input wire [63:0] data ,
output logic error
);
assign err = (^data != parity); //parity是$unit变量
endmodule
2.2.4 将包导入$unit的编码原则
//将包中特定子项导入到$unit中
import definitions::instruction_t;
module ALU (
input instruction_t IW ,
input logic clock ,
output logic [31:0] result
);
endmodule
//使用通配符将包导入到$unit中
import definitions::*;
module ALU (
input instruction_t IW ,
input logic clock ,
output logic [31:0] result
);
endmodule
/********************************带条件编译的包********************************/
`ifndef DFFS_DONE //如果已编译标志没设置
`define DFFS_FONE //设置该标志
package definitions;
parameter VERSION="1.1";
typedef enum {ADD,SUB,MUL} opcodes_t;
typedef struct {
logic [31:0] a ;
logic [31:0] b ;
opcodes_t opcode ;
} instructions_t; //自定义一个变量类型instructions_t,
function automatic [31:0] multiplier(
input [31:0] a,
input [31:0] b
);
//用户定义的32位乘法代码从这开始
return a*b;//抽象的乘法器(无错误检测)
endfunction //定一个函数,输入两个32位数,返回它们相乘的结果,并截取低32位
endpackage
`endif
每个包都应该带条件编译,可以有效避免重复声明变量
/********************************包含条件编译包文件的设计文件********************************/
`include "package\2.2.6.sv" //编译包文件
module ALU (
input instructions_t IW ,
input logic clock ,
output logic [31:0] result
);
always_comb begin
case(IW.opcode)
ADD:begin
result = IW.a + Iw.b;
end
SUB:begin
result = IW.a - IW.b;
end
MUL:begin
result = multiplier(IW.a,IW.b);
end
endcase
end
endmodule
/********************************包含条件编译包文件的测试平台文件********************************/
`include "package\2.2.6.sv"
module tb();
instructions_t test_word ;
logic [31:0] result ;
logic clock = 0 ;
ALU u_ALU(
.IW ( IW ),
.clock ( clock ),
.result ( result )
);
always #10 clock = ~clock;
initial begin
@(negedge clock)
test_word.a = 5 ;
test_word.b = 7 ;
test_word.opcode = ADD ;
@(negedge clock)
$display("alu_out = %d (expected 12)",result);
$finish;
end
endmodule
2.3 未命名语句块中的声明
2.3.1 未命名块中的局部变量
SystemVerilog扩展了Verilog,允许在未命名块中声明变量。
2.4 仿真时间单位和精度
2.4.1 Verilog中的timescale
timsescale
是告诉仿真工具应该以什么仿真精度去跑,而且与读入timescale
的顺序有关。
2.4.2 包含时间单位的时间值
SystemVerilog扩展了Verilog,可以给时间值指定时间单位
forever #5ns clock = ~clock
时间值和时间单位之间不能有空格
2.4.3 范围级(scope-level)时间单位和精度
使用关键字timeunit
和timeprecision
进一步增强时间单位的说明
module chip(…);
timeunit 1ns;
timeprecision 10ps;
…
endmodule
2.4.4 时间单位和精度搜索次序
如果时间值带单位,则使用指定的单位
否则,使用在模块、接口和程序块内部指定的时间单位和精度
否则,如果模块或接口声明嵌入到其他的模块和接口内,使用父模块或接口指定的时间单位和精度。
否则,使用模块编译时,有效的
`timescale
时间单位和精度否则,使用在编译单元域中定义的实践单位和精度
否则,使用仿真器默认的时间单位和精度
/********************************时间单位和精度的混合声明********************************/
timeunit 1ns; //外部声明的时间单位和精度
timeprecision 1ns;
module my_chip (…);
timeprecision 1ps; //局域精度(优先于外部精度)
always @(posedge data_request)begin
#2.5 send_packet; //使用外部单位和局部精度
#3.75ns check_crc; //使用指定的单位
end
task send_packet();
…
endtask
task check_crc();
…
endtask
endmodule
`timescale 1ps/1ps //timescale指令指定的单位和精度,优于外部声明
module FSM(…)
timeunit 1ns; //局部声明优先于timescale的指定
always @(State)begin
#1.2 case(State) //使用局部声明的单位和`timescale指定的精度
WAITE:#20ps …; //使用此处指定的单位
endcase
end
endmodule
三、SystemVerilog文本值和数据类型
3.1 增强的文本值赋值
verilog中赋值全1
data = ~0
或data = -1
SystemVerilog中全1
data = 'b1
SystemVerilog中全0
data = 'b0
SystemVerilog中全x
data = 'bx
SystemVerilog中全z
data = 'bz
3.2 `define增强
3.2.1 字符串内的宏变量替换
`define print(v)\
$display(`"variable v = %h`",v);
`print(data);
//本例中,宏`define将扩展为
$display("variable data = %h",data);
//verilog字符串中嵌入双引号必须加转义符\,在SystemVerilog中转义符变成了`\`
`define print(v)\
$display(`"variable `\`"v`\`" = %h`",v);
`print(data);
//本例中,宏`define将扩展为
$display("variable \"data\" = %h",data);
3.2.2 通过宏建立标识符名
在无文本替换的源文件中,声明可以是
bit d00_bit;wand d00_net = d00_bit;
bit d01_bit;wand d01_net = d01_bit;
…… //对每一位都重复这个操作,重复60多次
bit d62_bit;wand d62_net = d62_bit;
bit d63_bit;wand d63_net = d63_bit;
使用SystemVerilog对`define
的增强,代码可简化为
`define TWO_STATE_NET(name) bit name``_bit;\
wand name``_net = name``_bit;
`TWO_STATE_NET(d00)
`TWO_STATE_NET(d01)
……
`TWO_STATE_NET(d62)
`TWO_STATE_NET(d63)
3.3 SystemVerilog变量
3.3.1 对象类型和数据类型
SystemVerilog标准定义设计中的信号同时具有类型和数据类型。类型指示信号是线网还是变量,数据类型指示线网或变量的值系统:对两态数据类型是0或1,对四态数据类型是0、1、Z或X。logic
定义的对象是四态数据类型。变量类型可以是两态的也可以是四态的,线网只能是四态的。
3.3.2 SystemVerilog四态变量
logic
与reg
用法非常类似
3.3.3 SystemVerilog两态变量
bit
——1位两态整数byte
——8位两态整数,类似于char
shortint
——16位两态整数,类似于short
int
——32位两态整数,类似于int
longint
——64位两态整数,类似于longlong
四态类型的X和Z赋值给两态类型的数据时,会转换为逻辑0
void
——表示五存储,void
类型可以用在标签联合体中以及定义无返回值的函数real
——与double
类似,双精度浮点数shortreal
——与float
类似,单精度浮点数
3.3.4 显式及隐式变量和线网类型
所有数据类型前都可以加var
,没别的作用,就是好看,让人能知道这里声明了变量,增加代码可读性
3.4 在RTL模型中使用两态类型
3.4.1 两态类型的特点
四态值到两态值的转换
3.4.2 两态类型和四态类型仿真
3.4.3 在case语句中使用两态类型
不如四态类型能检测错误
3.5 数据类型规则的放宽
在SystemVerilog中,任何数据类型的变量都可以通过下列方式赋值,但是每个变量只能用其中的一种。原因是变量不能被多个源驱动
在任意的
initial
或always
过程块中赋值在单个
always_comb
,always_ff
或always_latch
过程块中赋值通过单个的持续赋值语句赋值
通过单个模块或原语的
output/inout
端口驱动赋值
/********************************变量的放宽应用********************************/
module compare (
input logic [63:0] a ,
input logic [63:0] b ,
output logic lt ,
output logic eq ,
output logic gt
);
always_comb begin
if(a < b)begin
lt = 1'b1; //过程赋值
end
else begin
lt = 1'b0;
end
end
assign gt = (a > b); //持续赋值
comparator u1(eq,a,b); //模块实例化
endmodule
module comparator (
input logic [63:0] a ,
input logic [63:0] b ,
output logic eq
);
always_comb begin
eq = (a ==b );
end
endmodule
3.6 有符号和无符号修饰符
signed
和unsigned
只能跟在数据类型声明的后面,例如int signed [31:0] s1
3.7 静态和自动变量
static
——静态变量关键字
automatic
——自动变量关键字
module一级声明变量不能显式地声明位static或automatic,模块级所有变量都是静态的
两个任务同时调用静态变量和动态变量时,静态变量会两个任务共用,自动变量针对每个任务创建一个变量。两个任务中的这个变量互不相关。
3.7.1 静态变量和自动变量的初始化
verilog变量的内嵌初始化
Verilog只允许在模块级声明的变量进行内嵌初始化,在任务、函数、begin…end块及fork…join块中声明的变量不能在声明时置初始值
SystemVerilog的内嵌初始化
SystemVerilog对Verilog进行了扩展,在任务、函数声明的变量可以有内嵌初始值
3.7.2 静态和自动变量的使用原则
在
always
和initial
块中,如果无内嵌初始化则使用静态变量,而需要内嵌初始化的使用自动变量。使用带内嵌初始化的变量,所表现的行为最直观,因为过程块每次被重新执行,自动变量都会重新初始化如果一个任务和函数是可重入的,则应该设成自动的,变量也应该是自动的,除非有特殊的原因需要在两次调用之间保持变量的值,一个简单的例子,如果需要用一个变量来记录自动任务或者函数被调用的次数,则这个变量应该是静态的
如果一个任务和函数用来描述硬件的独立部分,并且不是可重入的,那么应该把它声明为静态的,任务和函数中的所有变量也应该是静态的
3.8 变量初始化的确定性
3.8.1 初始化确定机制
SystemVerilog内嵌初始化优先于在仿真时刻0执行的事件。
测试平台应该将变量初始化为无效状态,比如复位信号低电平有效,则复位信号初始化为高电平
3.8.2 时序逻辑的异步输入初始化
/********************************在仿真时刻0,使用两态数据类型施加复位********************************/
module counter (
input wire clock ,
input wire rst_n ,
output logic [15:0] count
);
always_ff @( posedge clock or negedge rst_n ) begin
if(!rst_n)begin //rst_n低电平有效
count <= 0;
end
else begin
count <= count + 1;
end
end
endmodule
module test();
wire [15:0] count ;
bit clock ;
bit rst_n = 1 ; //rst_n初始化为无效值
counter u_counter(
.clock ( clock ),
.rst_n ( rst_n ),
.count ( count )
);
always #10 clock = ~clock;
initial begin
rst_n = 0; //在0时刻插入低电平有效的复位
#2 rst_n = 1; //在clock上升沿之前去除复位
$display("\n count = %0d (expect 0)\n",count);
#1 finish;
end
endmodule
3.9 强制类型转换
3.9.1 静态转换(编译时转换)
<type>'(<expression>)
——将一个值强制转换成任何数据类型,包括用户自定义类型。如:7 + int'(2.0 * 3.0)
将(2.0*3.0)
的结果强制转换成整形,然后加7<size>'(<expression>)'
——将一个值强制转换成任意向量宽度,如:
logic [15:0] a,b,y;
y = a + b**16'(2); //将文本值2强制转换为16位宽
<size>'(<expression>)'
——将一个值转换成有符号数或无符号数,如:
shortint a,b;
int y;
y = y - signed'({a,b});//将a,b拼接结果强制转换为有符号值
3.9.2 动态强制类型转换
SystemVerilog提供了一个新的系统函数cast,这是动态的,在运行时进行待转换数值的检查。系统函数cast有两个变量,一个目标变量和一个源变量,语法如下:$cast(dest_var,source_exp)
。举个例子:
int radius,area;
always @(posedge clock)begin
$cast(area,3.1415*radius**2);//强制转换操作符的结果被转换为area类型
3.10 常数
Verilog中的常数,只能在模块、静态任务和静态函数中声明:
parameter
是一个可以在确立(elaboration)时使用的defparam
或者内嵌参数重定义定性重新定义的常数specparam
是一个可以在确立时从SDF文件中重定义的常数localparam
是确立期常数,不能重定义。但是它的值可以基于其他常数
SystemVerilog扩展的常数const
关键字,const形式的常数直到elaboration后才被赋值,因此可以:
在自动任务和函数等动态环境中声明
被赋予一个线网或者变量值而非常数表达式
被赋予一个对象值,这个值可以在任何设计层次定义
const
常数的声明必须包含数据类型
const logic [23:0] C1 = 7; //24位常数
const int C2 = 15; //32位常数
const real C3 = 3.14; //实数常数
const C4 = 5; //错误,没有数据类型
四、用户自定义和枚举数据类型
4.1 用户自定义类型
4.1.1 局部typedef
声明
4.1.2 共享typedef
声明
`ifndef DFFS_DONE
`define DFFS_DONE
package chip_types;
`ifdef TWO_STATE
typedef bit dtype_t;
`else
typedef logic dtype_t;
`endif
endpackage
`endif
/********************************直接从包中引用用户自定义类型********************************/
import chip_types::*;
module counter (
input dtype_t clock ,
input dtype_t rst_n ,
output dtype_t [15:0] count
);
always @(posedge clock or negedge rst_n)begin
if(!rst_n)begin
count <= 0;
end
else begin
count <= count + 1;
end
end
endmodule
4.1.3 用户自定义类型的命名习惯
使用"_t"结尾作为用户自定义类型的结尾
4.2 枚举数据类型
/********************************用verilog的`define和parameter常数建模的状态机********************************/
`define FETCH 3'h0
`define WRITE 3'h1
`define ADD 3'h2
`define SUB 3'h3
`define MULT 3'h4
`define DIV 3'h5
`define SHIFT 3'h6
`define NOP 3'h7
module controller (
input wire [2:0] instruction ,
input wire clock ,
input wire rst_n ,
output logic read ,
output logic write
);
parameter WAITE = 0 ;
parameter LOAD = 1 ;
parameter STORE = 2 ;
logic [1:0] curr_state;
logic [1:0] next_state;
always_ff @( posedge clock or negedge rst_n ) begin
if(!rst_n)begin
curr_state <= WAITE;
end
else begin
curr_state <= next_state;
end
end
always_comb begin
case(curr_state)
WAITE:begin
next_state = LOAD;
end
LOAD:begin
next_state = STORE;
end
STORE:begin
next_state = WAITE;
end
endcase
end
always @(curr_state or instruction) begin
read = 0;
write = 0;
if(curr_state == LOAD && instruction == `FETCH)begin
read = 1;
end
else if(curr_state == STORE && instruction == `WRITE)begin
write = 1;
end
end
endmodule
/********************************使用枚举类型建模的状态机********************************/
package chip_types;
typedef enum {FETCH,WRITE,ADD,SUB,MULT,DIV,SHIFT,NOP} instruction_t;
endpackage
import chip_types::*;
module controller (
input instruction_t instruction ,
input wire clock ,
input wire rst_n ,
output logic read ,
output logic write
);
enum {WAITE,LOAD,STORE} curr_state,next_state;
always_ff @( posedge clock or negedge rst_n ) begin
if(!rst_n)begin
curr_state <= WAITE;
end
else begin
curr_state <= next_state;
end
end
always_comb begin
case(curr_state)
WAITE:begin
next_state = LOAD;
end
LOAD:begin
next_state = STORE;
end
STORE:begin
next_state = WAITE;
end
endcase
end
always_comb begin
if(curr_state == LOAD && instruction == FETCH)begin
read = 1;
end
else if(curr_state == STORE && instruction == WRITE)begin
write = 1;
end
else begin
read = 0;
write = 0;
end
end
endmodule
4.2.1 枚举类型标签序列
创建一个枚举列表,带有标签RESET,S0S5,W6W9:
enum {RESET,S[5],W[6:9]} state;
4.2.2 枚举类型标签作用域
4.2.3 枚举类型值
可以显式给标签赋值
enum {A = 1,B = 2,C} list
C会被自动赋值3,因为C= B+1
4.2.4 枚举类型的基类
默认基类是int,32位两态整形。
可以显式定义基类,比如:enum logic [1:0] {WAITE,LOAD,READY} state;
4.2.5 自定义和匿名枚举
typedef enum {WAITE,LOAD,READY} state_t
4.2.6 枚举类型操作的强类型检验
枚举类型是半强类型,一个枚举类型只可以进行下列赋值:
枚举类型列表中的一个标签
同类枚举类型的其他变量
通过cast转换成枚举类型变量的数值
4.2.7 将表达式强制转换为枚举类型
4.2.8 枚举类型的专用系统任务和方法
<枚举变量名>.first——返回指定变量枚举列表中的第一个成员的值
<枚举变量名>.last——返回枚举列表最后一个成员的值
<枚举变量名>.next(<N>)——返回枚举列表中下一个成员的值。可以用一个整数值作为
next
的参数。这种情况下,从枚举列表的当前位置算起,返回后面第N个成员的值。如果到达了枚举列表的末尾,则会返回到列表的开头。如果枚举变量的当前值不在枚举列表中,则会返回列表中第一个成员的值。<枚举变量名>.prev(<N>)——同
next
,但是是往前<枚举变量名>.num——返回变量的枚举列表中元素个数
<枚举变量名>.name——返回枚举变量中代表这个值的字符串。如果这个值不在枚举变量列表中,则返回一个空字符串
/********************************使用专用方法循环访问枚举类型列表********************************/
module confidence_counter (
input logic synced ,
input logic compare ,
input logic clock ,
input logic rst_n ,
output logic in_sync
);
typedef enum {cnt[0:15]} state_t;
state_t curr_state;
state_t next_State;
always_ff @( posedge clock or negedge rst_n ) begin
if(!rst_n)begin
curr_state <= cnt0;
end
else begin
curr_state <= next_State;
end
end
always_comb begin
case(curr_state)
cnt0:begin
if(compare && synced)begin
next_State = curr_state.next;
end
else begin
next_State = curr_state;
end
end
cnt1:begin
if(compare && synced)begin
next_State = curr_state.next;
end
else if(compare && !synced)begin
next_State = curr_state.first;
end
else begin
next_State = curr_state;
end
end
cnt15:begin
if(compare && !synced)begin
next_State = curr_state.prev(2);
end
else begin
next_State = curr_state;
end
end
default:begin
if(compare && synced)begin
next_State = curr_state.next;
end
else if(compare && !synced)begin
next_State = curr_state.prev(2);
end
else begin
next_State = curr_state;
end
end
endcase
end
always_ff @( posedge clock or negedge rst_n ) begin
if(!rst_n)begin
in_synce <= 0;
end
else begin
if(curr_state == cnt8)begin
in_sync <= 1;
end
else (curr_state == cnt0)begin
in_sync <= 0;
end
end
end
endmodule
4.2.9 打印枚举类型
/********************************打印枚举类型变量的值和名称********************************/
module FSM (
input logic clock ,
input logic rst_n ,
output logic [3:0] control
);
typedef enum logic [2:0] {WAITE = 3'b001,LOAD = 3'b010,READY = 3'b100} state_t;
state_t curr_state;
state_t next_state;
always_ff @( posedge clock or negedge rst_n ) begin
if(!rst_n)begin
curr_state <= WAITE;
end
else begin
curr_state <= next_state;
end
end
always_comb begin
$display("\nCurrent state is %s(%b)",curr_state.name,curr_state);
case(curr_state)
WAITE:begin
next_state = LOAD;
end
LOAD:begin
next_state = READY;
end
READY:begin
next_state = WAITE;
end
endcase
$display("Next state will be %s(%b)",next_state.name,next_state);
end
assign control = curr_state;
endmodule
五、数组、结构体和联合体
5.1 结构体
5.1.1 结构体声明
var struct { //结构体变量
logic [31:0] a,b ;
logic [7:0] opcode ;
logic [23:0] address ;
}Instruction_Word_Var;
wire struct { //结构体线网
logic [31:0] a,b ;
logic [7:0] opcode ;
logic [23:0] address ;
}Instruction_Word_Net;
struct { //结构体变量
logic [31:0] a,b ;
logic [7:0] opcode ;
logic [23:0] address ;
}Instruction_Word_var;
typedef struct { //结构体定义
logic [31:0] a,b ;
logic [7:0] opcode ;
logic [23:0] address ;
}instruction_word_t;
instruction_word_t IW; //结构体分配存储区
struct { //匿名结构体
logic [31:0] a,b ;
logic [7:0] opcode ;
logic [23:0] address ;
}isntruction;
5.1.2 结构体赋值
结构体初始化
typedef struct {
logic [31:0] a,b ;
logic [7:0] opcode ;
logic [23:0] address ;
}instruction_word_t;
instruction_word_t IW = '{100,3,8'hFF,0};
SystemVerilog用'{}
符号包含数值列表,C语言用{}
结构体成员赋值
typedef struct {
logic [31:0] a,b ;
logic [7:0] opcode ;
logic [23:0] address ;
}instruction_word_t;
instruction_word_t IW;
always_ff @( posedge clock or negedge rst_n ) begin
if(!rst_n)begin
IW.a = 100; //引用结构体成员
IW.b = 5;
IW.opcode = 8'hFF;
IW.adress = 0;
end
else begin
…
end
end
将结构体表达式赋给结构体
typedef struct {
logic [31:0] a,b ;
logic [7:0] opcode ;
logic [23:0] address ;
}instruction_word_t;
instruction_word_t IW;
always_ff @( posedge clock or negedge rst_n ) begin
if(!rst_n)begin
IW = '{100,5,8'hFF,0}; //根据顺序赋值
end
else begin
…
end
end
always_ff @( posedge clock or negedge rst_n ) begin
if(!rst_n)begin
IW = '{address:0,opcode:8'hFF,a:100,b:5}; //根据名称赋值
end
else begin
…
end
end
结构体表达式的默认值
typedef struct {
real r0,r1 ;
int i0,i1 ;
logic [7:0] opcode ;
logic [23:0]address ;
} instruction_word_t;
instruction_word_t IW;
always_ff @( posedge clocl or negedge rst_n ) begin
if(!rst_n)begin
IW = '{real:1.0,default:0};
//指定real类型的成员默认值为1.0
//指定其他成员默认值0
end
else begin
…
end
end
5.1.3 压缩和非压缩结构体
struct packed{
logic valid ;
logic [7:0] tag ;
logic [31:0]data ;
}data_word;
压缩结构体被当作向量存储
5.1.4 通过端口传递结构体
5.1.5 将结构体作为自变量传递至任务和函数
5.2 联合体
联合体只储存一个元素,但是这个元素可以有多种表示方法,每种表示可以是不同的数据类型,联合体的声明语法类似于结构体,联合体的成员的引用也跟结构体一样。
union {
int i ;
int unsigned u ;
} data;
…
data.i = -5;
$display("data is %d",data.i);
data.u = -5;
$display("Now data is %d",data.u);
5.2.1 非压缩联合体
不可综合
5.2.2 标签联合体
可以给联合体的某个类型设置隐含标签,比如:data = tagged i 5
,这样data.i就被打上了隐含标签,下一次如果读data.r软件就会报错
5.2.3 压缩联合体
压缩联合体成员的位数必须相同,只能储存整数值
压缩的标签联合体的位数可以不相同,但是也只能储存整数值
5.2.4 综合指导
压缩联合体和压缩的标签联合体是可综合的
5.2.5 使用结构体和联合体的一个例子
/********************************使用结构体和联合体********************************/
`ifndef DFFS_DONE
`define DFFS_DONE
package definitions
typedef enum {ADD,SUB,MULT,DIV,SL,SR} opcode_t;
typedef enum {UNSIGNED,SIGNED} operand_type_t;
type union packed{
logic [31:0] u_data;
logic signed [31:0] s_data;
}data_t;
typedef struct packed {
opcode_t opc ;
operand_type_t op_type ;
data_t op_a ;
data_t op_b ;
} instr_t;
endpackage
`endif
import definitions::*;
module alu (
input instr_t IW ,
output data_t alu_out
);
always_comb begin
if(IW.op_type == SIGNED)begin
case(IW.opc)
ADD :alu_out.s_data = IW.op_a.s_data + IW.op_b.s_data ;
SUB :alu_out.s_data = IW.op_a.s_data - IW.op_b.s_data ;
MULT:alu_out.s_Data = IW.op_a.s_data * IW.op_b.s_data ;
DIV :alu_out.s_Data = IW.op_a.s_data / IW.op_b.s_data ;
SL :alu_out.s_Data = IW.op_a.s_data <<< 2 ;
SR :alu_out.s_Data = IW.op_a.s_data >>> 2 ;
endcase
else begin
case(IW.opc)
ADD :alu_out.u_data = IW.op_a.u_data + IW.op_b.u_data ;
SUB :alu_out.u_data = IW.op_a.u_data - IW.op_b.u_data ;
MULT:alu_out.u_Data = IW.op_a.u_data * IW.op_b.u_data ;
DIV :alu_out.u_Data = IW.op_a.u_data / IW.op_b.u_data ;
SL :alu_out.u_Data = IW.op_a.u_data <<< 2 ;
SR :alu_out.u_Data = IW.op_a.u_data >>> 2 ;
endcase
end
end
end
endmodule
5.3 数组
5.3.1 非压缩数组
Verilog数组声明的基本语法是:<data_type><vector_size><array_name><array_dimensions>
Verilog限制一次只能访问数组的一个元素,或者一个元素的1位或者部分位
logic [7:0] data [1024]
等效于logic [7:0] data [0:1023]
5.3.2 压缩数组
logic [3:0][7:0] data //2维压缩数组
存储方式如图所示,存储为一个向量
5.3.3 使用压缩和非压缩数组
非压缩数组用于建模:
下列数据类型的数组:
byte
、int
、integer
、real
、非压缩结构体、非压缩联合体以及其他非位形式的数据类型通常每次只访问一个元素的数组,例如RAM和ROM
压缩数组用于建模:
由只有1位的数据类型组成的向量
需要子段访问的向量
5.3.4 声明时对数组进行初始化
/********************************压缩数组初始化********************************/
logic [3:0][7:0] a = 32'h0 ;//向量赋值
logic [3:0][7:0] b = {16'hz,16'h0} ;//拼接操作符
logic [3:0][7:0] c = {16{2'b01}} ;//复制操作符
/********************************非压缩数组初始化********************************/
int d1[0:1][0:3] = '{'{7,3,0,5},'{2,0,1,6}};
//d1[0][0] = 7
//d1[0][1] = 3
//d1[0][2] = 0
//d1[0][3] = 5
//d1[1][0] = 2
//d1[1][1] = 0
//d1[1][2] = 1
//d1[1][3] = 6
int d1[0:1][0:3] = '{2{7,3,0,5}};
//d1[0][0] = 7
//d1[0][1] = 3
//d1[0][2] = 0
//d1[0][3] = 5
//d1[1][0] = 7
//d1[1][1] = 3
//d1[1][2] = 0
//d1[1][3] = 5
5.3.5 数组赋值
Verilog支持两种方式的非压缩数组赋值:
给一个元素赋值
给一个元素的1位或者部分位赋值
SystemVerilog扩展了两种新的非压缩数组赋值方法:
用一个值序列给整个数组赋值
用一个值序列给数组的一段赋值
5.3.6 数组复制
压缩数组到压缩数组:直接赋值,多余的位截掉,缺少的位补0
非压缩数组到非压缩数组:两个结构完全一样的非压缩数组才能复制,否则需要通过位流转换
压缩数组到非压缩数组:只能通过位流转换
非压缩数组到压缩数组:只能通过位流转换
5.3.7 使用位流转换复制数组和结构体
/********************************位流转换********************************/
typedef int data_t [3:0][7:0];//非压缩类型
data_t a;//非压缩数组
int b[1:0][3:0][3:0];//非压缩数组
a = data_t'(b);//将非压缩数组赋给不同结构的非压缩数组
5.3.8 由数组构成的数组
5.3.9 数组中使用用户自定义类型
5.3.10 数组通过端口及任务和函数的传送
相比于Verilog,SystemVerilog允许非压缩数组通过模块端口传送,或者传进/传出函数和任务
5.3.11 结构体和联合体构成的数组
压缩数组中的结构体和联合体必须也是压缩的
5.3.12 结构体和联合体中的数组
压缩的联合体和结构体中只能包含压缩数组
5.3.13 综合指导
5.3.14 使用数组的例子
/********************************使用由结构体构成的数组对指令寄存器建模********************************/
`ifndef DFFS_DONE
`define DFFS_DONE
package definitions;
typedef enum {ADD,SUB,MULT,DIV,SL,SR} opcode_t;
typedef enum {UNSIGNED,SIGNED} operand_type_t;
typedef union packed {
logic [31:0] u_data;
logic signed[31:0] s_data;
}data_t;
typedef struct packed {
opcode_t opc ;
operand_type_t op_type ;
data_t op_a ;
data_t op_b ;
} instr_t;
endpackage
`endif
import definitions::*;
module instruction_register(
input data_t operand_a ,
input data_t operand_b ,
input operand_type_t op_type ,
input opcode_t opcode ,
input logic [4:0] write_pointer ,
output instr_t [0:31] instr_reg //结构体构成的压缩数组
);
always_comb begin
instr_reg[write_pointer].op_type = op_type ;
instr_reg[write_pointer].opc = opcode ;
//用op_type来确定存储在输入操作数联合体中的操作数类型
if(op_type == SIGNED)begin
instr_reg[write_pointer].op_a.s_data = operand_a.s_data;
instr_reg[write_pointer].op_b.s_data = operand_b.s_data;
end
else begin
instr_reg[write_pointer].op_a.u_data = operand_a.u_data;
instr_reg[write_pointer].op_b.u_data = operand_b.u_data;
end
end
endmodule
5.4 foreach数组循环结构体
5.5 用于数组查询的系统函数
$dimensions(array_name)
返回数组的位数(如果对象不是数组则返回0)$left(array_name,dimension)
返回指定维度的最高有效位(msb)。比如:
logic [1:2][7:0] word [0:3][4:1];
$left(word,1)返回0
$lest(word,2)饭回4
$lest(word,3)返回1
$lest(word,4)返回7
$right(array_name,dimension)
返回指定维度的最低有效位(lsb)。$low(array_name,dimension)
返回指定维度的最低位数,可能是msb或lsb。比如:
logic [1:2][7:0] word [0:3][4:1];
$low(word,1)返回0
$low(word,2)饭回1
$low(word,3)返回1
$low(word,4)返回0
$high(array_name,dimension)
返回指定维度的最高位数,可能是msb或lsb。$size(array_name,dimension)
返回指定维数的总个数,即$high - $low + 1
increment(array_name,dimension)
对于指定的维度,如果$left
大于或等于$right
则返回1,否则返回-1
5.6 $bits位宽系统函数
$bits(expression)
返回任意表达式的位数,比如:
bit [63:0] a ;
logic [63:0] b ;
wire [3:0][7:0] c [0:15];
struct packed{
byte tag ;
logic [31:0] addr;
}d;
$bits(a)返回64
$bits(b)返回64
$bits(c)返回512
$bits(d)返回40
$bits(a + b)返回128
5.7 动态数组、联合数组、稀疏数组和字符串
在SystemVerilog fot Verification中介绍
六、SystemVerilog过程块、任务和函数
6.1 Verilog通用目的always过程块
6.2 SystemVerilog特有的过程块
6.2.1 组合逻辑过程块
always_comb
过程块表示建立组合逻辑模型
/********************************使用always过程块建模的有限状态机********************************/
import chip_types::*
module controller(
input instruction_t instruction ,
input logic clock ,
input logic rst_n ,
output logic read ,
output logic write
);
typedef enum {WAITE,LOAD,STORE} state_t;
state_t curr_state;
state_t next_state;
always @(posedge clock or negedge rst_n)begin
if(!rst_n)begin
curr_state <= WAITE;
end
else begin
curr_state <= next_state;
end
end
always @(curr_state)begin
case(curr_state)
WAITE:begin
next_state = LOAD;
end
LOAD:begin
next_state = STORE;
end
STORE:begin
next_state = WAITE;
end
endcase
end
always @(curr_state or instruction)begin
read = 0;
write = 0;
if(curr_state == LOAD && instruction == FETCH)begin
read = 1;
end
else if(curr_state == STORE && instruction == WRITE)begin
write = 1;
end
end
endmodule
如果next_state
默认为WAITE
,那么curr_state
的值永远不会变化,那么always @(curr_state)
的敏感列表不会变化,状态机就锁住了
/********************************使用always_comb过程块建模的状态机********************************/
import chip_types::*;
module controller(
input instruction_t instruction ,
input logic clock ,
input logic rst_n ,
output logic read ,
output logic write
);
typedef enum {WAITE,LOAD,STORE} state_t;
state_t curr_State;
state_t next_state;
always_ff @( posedge clock or negedge rst_n ) begin
if(!rst_n)begin
curr_State <= WAITE;
end
else begin
curr_State <= next_state;
end
end
always_comb begin
case(curr_State)
WAITE:begin
next_state = LOAD;
end
LOAD:begin
next_state = STORE;
end
STORE:begin
next_state = WAITE;
end
endcase
end
always_comb begin
read = 0;
write = 0;
if(curr_State == WAITE && instruction == FETCH)begin
read = 1;
end
else if(curr_State == STORE && instruction == WRITE)begin
write = 1;
end
end
endmodule
6.2.2 锁存逻辑过程块
always_latch
表示过程块描述的是基于锁存器的逻辑
/********************************使用always_latch过程块锁存输入脉冲********************************/
module register_reader (
input wire clk ,
input wire ready ,
input wire rst_n ,
output logic [4:0] read_pointer
);
logic enable ; //计数器的内部使能信号
logic overflows ; //内部的计数器溢出标志
always_latch begin //锁存输入ready
if(!rst_n)begin
enable <= 0;
end
else if(ready)begin
enable <= 1;
end
else if(overflow)begin
enable <= 0
end
end
always_ff @( posedge clk or negedge rst_n ) begin//5位计数器
if(!rst_n)begin
{overflow,read_pointer} <= 0;
end
else if(enable)begin
{overflow,read_pointer} <= read_pointer + 1;
end
end
endmodule
6.2.3 时序逻辑过程块
always_ff
表示设计的意图是描述可综合的时序逻辑
6.3 对任务和函数的改进
6.3.1 任务和函数的隐式语句组
function
里面不需要再加begin…end
(感觉好像没啥用,还是加了好看)
6.3.2 返回函数值
Verilog返回与函数名相同的变量的值,SystemVerilog添加了return
语句,有return
返回return
的内容,没有return
返回函数名变量值
6.3.3 在任务和函数结束前返回
return
6.3.4 空函数
可以声明函数为void
类型,这样的函数还可以有输入输出,感觉就像是task
6.3.5 使用名称传递任务/函数的参数
Verilog根据顺序传递参数,SystemVerilog增加了用形式参数的名称来传递参数,与端口实例化时端口连接的语法一样。
6.3.6 增强型函数形式参数
可以定义输入输出形式参数
6.3.7 无形式参数的函数
完全就是task
嘛
6.3.8 形式参数的缺省方向和类型
输入可以缺省,默认是输入
6.3.9 缺省的形式参数值
function int increment (int count = 0, step = 1);
increment = count + step;
endfunction
调用函数increment
的时候,如果没有传递count和step的值,则他们使用缺省值
6.3.10 数组、结构体和联合体作为形式参数
SystemVerilog允许非压缩数组、压缩或非压缩结构体和压缩、非压缩或标签联合体传递进/出任务和函数
6.3.11 用引用取代复制来传递参数
ref
——只有自动任务和函数可以具有ref
参数,引用调用的信息const ref
——只读引用参数,禁止任务或函数改动信息的内容
好处是调用的是真时值,而不是函数被调用的时候的值
6.3.12 命名的任务和函数结尾
endfunction
和endtask
后面可以加个冒号,跟函数或任务的名字,有助提升代码可读性
6.3.13 空任务和函数
七、过程语句
7.1 新操作符
7.1.1 递增和递减操作符
递增和递减操作符都是阻塞赋值。(感觉不如不用,为难自己,可以不用,但是不能不知道)
7.1.2 赋值操作符
/********************************使用SystemVerilog的赋值操作符********************************/
import definitions::*;
module alu(
input instruction_t instr ,
output data_t alu_out
);
always_comb begin
if(instr.op_type == SIGNED)begin
alu_out.s_data = instr.op_a.s_data;
unique case (instr.opc)
ADD :alu_out.s_data += instr.op_b.s_data;
SUB :alu_out.s_data -= instr.op_b.s_data;
MULT:alu_out.s_data *= instr.op_b.s_data;
DIV :alu_out.s_data /= instr.op_b.s_data;
SL :alu_out.s_data <<<= 2 ;
SR :alu_out.s_data >>>= 2 ;
endcase
end
else begin
alu_out.u_data = instr.op_a.u_data;
unique case (instr.opc)
ADD :alu_out.u_data += instr.op_b.u_data;
SUB :alu_out.u_data -= instr.op_b.u_data;
MULT:alu_out.u_data *= instr.op_b.u_data;
DIV :alu_out.u_data /= instr.op_b.u_data;
SL :alu_out.u_data <<<= 2 ;
SR :alu_out.u_data >>>= 2 ;
endcase
end
end
endmodule
7.1.3 有无关通配符的相等操作符
7.1.4 设置成员操作符——inside
if (a inside {3'b001,3'b010,3'b100})
等效于if((a == 3'b001)||(a == 3'b010)||(a == 3'b100))
7.2 操作数改进
7.2.1 四态和两态类型数据的运算
7.2.2 类型强制转换
y = a + logint'(r**3)
7.2.3 尺寸强制转换
sum = a + 16'(5)
7.2.4 符号强制转换
sum = signed'(a) + signed'(a)
7.3 改进的for循环
7.3.1 for循环中声明的局部变量
always_ff @( posedge clk ) begin
for(bit[4:0]i = 0;i <= 15; i = i + 1)
…
end
在局部用完,这个变量就消失
7.3.2 多重for循环赋值
for(int i =0,int j = 0; i * j < 128;i = i + 1,j= j + 3)
7.3.3 for循环中生命的变量的层次化引用
7.4 底部检测的do…while循环
7.5 foreach数组循环结构
7.6 新的跳转语句——break、continue、return
7.7 改进的块名
/********************************使用未命名的嵌套的begin…end块的代码段********************************/
always_ff @( posedge clock or negedge rst_n ) begin
logic breakVar;
if(!rst_n)begin
……//将所有输出复位
end
else begin
case(SquatState)
wait_rx_valid:begin
Rxready <= 'b1;
breakVar = 1;
for(int j = 0; j < NumRx; j = j + 1)begin
for(int i = 0; i < NumRx; i = i + 1)begin
if(Rxvalid[I] && RoundRobin[i] && breakVar)begin
ATMcell <= RxATMcell[i];
Rxready <= 0;
SquatState <= wait_rx_not_valid;
breakVar = 0;
end
end
end
end
……//处理其他SquatState状态
endcase
end
end
/********************************使用命名的begin…end块的代码段********************************/
always_ff @( posedge clock or negedge rst_n ) begin:FSM_procedure
logic breakVar;
if(!rst_n)begin:reset_logic
……//将所有输出复位
end:reset_logic
else begin:FSM_sequencer
case(SquatState)
wait_rx_valid:begin:rx_valid_state
Rxready <= 'b1;
breakVar = 1;
for(int j = 0; j < NumRx; j = j + 1)begin:loop1
for(int i = 0; i < NumRx; i = i + 1)begin:loop2
if(Rxvalid[I] && RoundRobin[i] && breakVar)begin:match
ATMcell <= RxATMcell[i];
Rxready <= 0;
SquatState <= wait_rx_not_valid;
breakVar = 0;
end:match
end:loop2
end:loop1
end:rx_valid_state
……//处理其他SquatState状态
endcase
end:FSM_sequencer
end:FSM_procedure
7.8 语句标号
begin:block1 //已命名的块
……
end:block1
block2:begin//带标号的块
……
end
7.9 改进的case语句
7.9.1 unique case条件判断
unique case
语句指定:
只有一个条件选项与条件表达式匹配
必须有一个条件选项与条件表达式匹配
7.9.2 priority case 语句
priority case
语句指定:
至少有一个条件选项的值与条件表达式匹配
如果有多个条件选项的值与条件表达式匹配,必须执行第一个匹配分支
7.9.3 unique、priority与parallel_case、full_case的对比
推荐使用unique
、priority
7.10 改进的if…else判断语句
7.10.1 unique if…else判断语句
无优先级
7.10.2 priority if判断语句
有优先级
八、有限状态机建模
8.1 使用枚举类型建立状态机模型
/********************************使用枚举类型建模的有限状态机********************************/
module traffic_light(
//Input
input wire sensor ,
input wire clock ,
input wire rst_n ,
input wire [15:0] green_downcnt ,
input wire [15:0] yellow_downcnt ,
output logic green_light ,
output logic yellow_light ,
output logic red_light
);
typedef enum logic[1:0] {RED,GREEN,YELLOW} state_t;
state_t curr_state;
state_t next_state;
always_ff @( posedge clock or negedge rst_n ) begin
if(!rst_n)begin
curr_state <= RED; //复位为红灯
end
else begin
curr_state <= next_state;
end
end
always_comb begin:set_next_state
next_state = curr_state;
unique case(curr_state)
RED: if(sensor) next_state = GREEN ;
GREEN: if(green_downcnt == 0) next_state = YELLOW ;
YELLOW: if(yellow_downcnt == 0) next_state = RED ;
endcase
end:set_next_state
always_comb begin:set_output
{green_light,red_light,yellow_light} = 3'b0;
unique case(curr_state)
RED: red_light = 1'b1;
GREEN: green_light = 1'b1;
YELLOW: yellow_light= 1'b1;
endcase
end:set_output
endmodule
8.1.1 使用枚举类型表示状态编码
/********************************指定使用枚举类型指明用one-hot码进行建模********************************/
module traffic_light(
input wire clock ,
input wire rst_n ,
input wire sensor ,
input wire [15:0] yellow_downcnt ,
input wire [15:0] green_downcnt ,
output logic green_light ,
output logic yellow_light ,
output logic red_light
);
typedef enum logic[2:0] { RED = 3'b001,
GREEN = 3'b010,
YELLOW= 3'b100 } state_t;
state_t curr_State;
state_t next_state;
always_ff @( posedge clock or negedge rst_n ) begin : set_curr_stsate
if(!rst_n)begin
curr_State <= RED;
end
else begin
curr_State <= next_state;
end
end
always_comb begin : set_next_state
next_state = curr_State;
unique case(curr_State)
RED: if(sensor) next_state = GREEN ;
GREEN: if(green_downcnt == 0) next_state = YELLOW ;
YELLOW: if(yellow_downcnt == 0) next_state = RED ;
endcase
end:set_next_state
always_comb begin : set_output
unique case(curr_State)
RED: red_light = 1'b1;
GREEN: green_light = 1'b1;
YELLOW: yellow_light= 1'b1;
endcase
end:set_output
endmodule
8.1.2 使用枚举类型的反向case语句(非常好的新写法,值得学习)
/********************************使用反向case语句风格用one-hot码进行编码********************************/
module traffic_light(
input wire clock ,
input wire rst_n ,
input wire sensor ,
input wire [15:0] yellow_downcnt ,
input wire [15:0] green_downcnt ,
output logic green_light ,
output logic yellow_light ,
output logic red_light
);
enum { R_BIT = 0, //状态寄存器中RED状态的索引
G_BIT = 1, //状态寄存器中GREEN状态的索引
Y_BIT = 2 }state_bit;
//将1移到表示每个状态的位上
typedef enum logic[2:0] { RED = 3'b001 << R_BIT,
GREEN = 3'b001 << G_BIT,
YELLOW= 3'b001 << Y_BIT} state_t;
state_t curr_State;
state_t next_state;
always_ff @( posedge clock or negedge rst_n ) begin : set_curr_stsate
if(!rst_n)begin
curr_State <= RED;
end
else begin
curr_State <= next_state;
end
end
always_comb begin : set_next_state
next_state = curr_State;
unique case(1'b1)
curr_State[R_BIT]: if(sensor) next_state = GREEN ;
curr_State[G_BIT]: if(green_downcnt == 0) next_state = YELLOW ;
curr_State[Y_BIT]: if(yellow_downcnt == 0) next_state = RED ;
endcase
end:set_next_state
always_comb begin : set_output
{red_light,green_light,yellow_light} <= 3'b000;
unique case(1'b1)
curr_State[R_BIT]: red_light = 1'b1;
curr_State[G_BIT]: green_light = 1'b1;
curr_State[Y_BIT]: yellow_light= 1'b1;
endcase
end:set_output
endmodule
Sunberst Design公司的Cliff Cummings有很多使用位移操作符来指定状态变量枚举值的巧妙编码技巧。其他FSM编码技巧可以在Cliff的网站上找到
8.1.3 枚举类型与unique case语句
8.1.4 指定未使用的状态值
unique case
不用加default
,这合乎语法规范吗?主要是加也加不了吧,最多加一个default: next_state = default_state
,其中default_state = 'bx'
8.1.5 将状态值赋给枚举类型变量
尽量使用枚举列表中的标签而不是具体数值对枚举变量赋值
8.1.6 对枚举类型变量的操作
8.2 在FSM模型中使用两态数据类型
8.2.1 使用两态类型和枚举类型对FSM复位
最好用四态枚举类型
九、层次化设计
9.1 模块原型
9.1.1 原型和实际定义
SystemVerilog要求extern module
声明的端口列表必须与模块的实际定义严格匹配,包括端口顺序和端口位数。
9.1.2 避免端口冗余声明
extern module counter #(
parameter N = 15
)(
input wire clock ,
input wire load ,
input wire rst_n ,
input wire [N:0] d ,
ouptut logic [N:0] cnt
);
endmodule
module counter(.*);
always_ff @(posedge clock or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(load)begin
cnt <= d;
end
else begin
cnt <= cnt + 1;
end
end
endmodule
9.2 命名的结束语句
9.2.1 命名的模块结尾
module
的嵌套就不学了吧,感觉很不规范的样子,可读性直线下降
十、接口
10.1 接口的概念
/********************************简单设计的Verilog模块互联********************************/
module top(
input wire clock ,
input wire rst_n ,
input wire test_mode
);
wire [15:0] data ;
wire [15:0] address ;
wire [15:0] program_address ;
wire [15:0] jump_address ;
wire [7:0] instruction ;
wire [7:0] next_instruction ;
wire [3:0] slave_instruction ;
wire slave_request ;
wire slave_ready ;
wire bus_request ;
wire bus_grant ;
wire mem_read ;
wire mem_write ;
wire data_ready ;
processor u_processor(
.inout data ( inout data ),
.bus_request ( bus_request ),
.slave_ready ( slave_ready ),
.address ( address ),
.slave_instruction ( slave_instruction ),
.slave_request ( slave_request ),
.bus_grant ( bus_grant ),
.mem_read ( mem_read ),
.mem_write ( mem_write ),
.instruction ( instruction ),
.clock ( clock ),
.rst_n ( rst_n ),
.test_mode ( test_mode ),
.jump_address ( jump_address )
);
slave u_slave(
.inout data ( inout data ),
.inout address ( inout address ),
.slave_instruction ( slave_instruction ),
.slave_request ( slave_request ),
.bus_grant ( bus_grant ),
.data_ready ( data_ready ),
.bus_request ( bus_request ),
.slave_ready ( slave_ready ),
.mem_read ( mem_read ),
.mem_write ( mem_write ),
.clock ( clock ),
.rst_n ( rst_n )
);
dual_port_ram u_dual_port_ram(
.inout data ( inout data ),
.address ( address ),
.mem_read ( mem_read ),
.mem_write ( mem_write ),
.data_ready ( data_ready ),
.program_address ( program_address ),
.p7:0 ( p7:0 )
);
test_generator u_test_generator(
.data ( data ),
.address ( address ),
.mem_read ( mem_read ),
.mem_write ( mem_write ),
.clock ( clock ),
.rst_n ( rst_n ),
.test_mode ( test_mode )
);
instruction_reg u_instruction_reg(
.jump_address ( jump_address ),
.next_instruction ( next_instruction ),
.clock ( clock ),
.rst_n ( rst_n ),
.program_address ( program_address ),
.instruction ( instruction )
);
endmodule
/********************************模块定义********************************/
module processor(
//main_bus端口
inout wire [15:0] data ,
input wire bus_request ,
input wire slave_ready ,
output reg [15:0] address ,
output reg [3:0] slave_instruction ,
output reg slave_request ,
output reg bus_grant ,
output wire mem_read ,
output wire mem_write ,
//其他端口
input wire [7:0] instruction ,
input wire clock ,
input wire rst_n ,
input wire test_mode ,
output reg [15:0] jump_address
);
……//模块功能代码
endmodule
module slave(
//main_bus端口
inout wire [15:0] data ,
inout wire [15:0] address ,
input wire [3:0] slave_instruction ,
input wire slave_request ,
input wire bus_grant ,
input wire data_ready ,
output reg bus_request ,
output reg slave_ready ,
output wire mem_read ,
output wire mem_write ,
//其他端口
input wire clock ,
input wire rst_n
);
……//模块功能代码
endmodule
module dual_port_ram(
//main_bus端口
inout wire [15:0] data ,
input wire [15:0] address ,
input tri0 mem_read ,
input tri0 mem_write ,
output wire data_ready ,
//其他端口
input wire [15:0] program_address ,
output reg p7:0[ data_b]
);
……//模块功能代码
endmodule
module test_generator(
//main_bus端口
output wire [15:0] data ,
output reg [15:0] address ,
output reg mem_read ,
output reg mem_write ,
//其他端口
input wire clock ,
input wire rst_n ,
input wire test_mode
);
……//模块功能代码
endmodule
module instruction_reg(
input wire [15:0] jump_address ,
input wire [7:0] next_instruction ,
input wire clock ,
input wire rst_n ,
output reg [15:0] program_address ,
output reg [7:0] instruction ,
);
……//模块功能代码
endmodule
10.1.1 Verilog模块端口的缺点
在多个模块中必须重复声明端口
在多个模块中通信协议也必须重复
在不同模块中有声明不匹配的风险
设计规范中的一个改动需要修改多个模块
10.1.2 SystemVerilog接口的优势
/********************************接口定义********************************/
interface main_bus;
wire [15:0] data ;
wire [15:0] address ;
logic [7:0] slave_instruction ;
logic slave_request ;
logic bus_grant ;
logic bus_request ;
logic slave_ready ;
logic data_ready ;
logic mem_ready ;
logic mem_write ;
endinterface
/********************************顶层网表********************************/
module top(
input wire clock ,
input wire rst_n ,
input wire test_mode
);
logic [15:0] program_address ;
logic [15:0] jump_address ;
logic [7:0] instruction ;
logic [7:0] next_instruction;
main_bus bus(); //接口实例化,实例名字是bus
processor procl(
//main_bus端口
.bus ( bus ),//接口连接
//其它端口
.junp_address ( jump_address ),
.instruction ( instruction ),
.clock ( clock ),
.rst_n ( rst_n ),
.test_mode ( test_mode )
);
slave slave1(
//main端口
.bus ( bus ),
//其他端口
.clock ( clock ),
.rst_n ( rst_n )
);
dual_port_ram ram(
//main_bus端口
.bus ( bus ),
//其他端口
.program_address( program_address ),
.data_b ( next_instruction )
);
test_generator test_gen(
//main_bus端口
.bus ( bus ),
//其他端口
.clock ( clock ),
.rst_n ( rst_n ),
.test_mode ( test_mode )
);
instruction_reg ir(
.program_address ( program_address ),
.instruction ( instruction ),
.jump_address ( jump_address ),
.next_instruction ( next_instruction ),
.clock ( clock ),
.rst_n ( rst_n )
);
endmodule
/********************************模块定义********************************/
module processor(
//main_bus的接口端口
main_bus bus,
input logic [7:0] instruction ,
input logic clock ,
input logic rst_n ,
input logic test_mode ,
output logic [7:0] jump_address
);
……//模块功能代码
endmodule
module slave(
//main_bus端口
main_bus bus ,
//其他端口
input wire clock ,
input wire rst_n
);
……//模块功能代码
endmodule
module dual_port_ram(
//main_bus端口
main_bus bus ,
//其他端口
input wire [15:0] program_address ,
output reg p7:0[ data_b]
);
……//模块功能代码
endmodule
module test_generator(
//main_bus端口
main_bus bus ,
//其他端口
input wire clock ,
input wire rst_n ,
input wire test_mode
);
……//模块功能代码
endmodule
module instruction_reg(
input wire [15:0] jump_address ,
input wire [7:0] next_instruction ,
input wire clock ,
input wire rst_n ,
output reg [15:0] program_address ,
output reg [7:0] instruction ,
);
……//模块功能代码
endmodule
10.1.3 接口的内容
使用接口可以:
在一个地方——接口中定义通信所需的各个信号和端口
在接口中定义通信协议
在接口中直接建立协议校验和其他验证程序
10.1.4 接口与模块的不同点
接口不可以包含设计层次
接口可以用作模块端口,表示模块间的通信通道
接口可以包含
modport
10.2 接口声明
/********************************接口定义********************************/
interface main_bus(
input logic clock ,
input logic rst_n ,
input logic test_mode
);
wire [15:0] data ;
wire [15:0] address ;
logic [7:0] slave_instruction ;
logic slave_request ;
logic bus_grant ;
logic bus_request ;
logic slave_ready ;
logic data_ready ;
logic mem_read ;
logic mem_write ;
endinterface //main_bus
/********************************顶层网表********************************/
module top(
input logic clock ,
input logic rst_n ,
input logic test_mode
);
logic [15:0] program_address ;
logic [15:0] jump_address ;
logic [7:0] instruction ;
logic [7:0] next_instruction ;
main_bus bus(
.clock ( clock ),
.rst_n ( rst_n ),
.test_mode ( test_mode )
);
processor procl(
.bus ( bus ),
.jump_address ( jump_address ),
.instruction ( instruction )
);
...
endmodule
/********************************使用.*连接的接口来简化复杂网表********************************/
interface main_bus(
input logic clock ,
input logic rst_n ,
input logic test_mode
);
wire [15:0] data ;
wire [15:0] address ;
logic [7:0] slave_instruction ;
logic slave_request ;
logic bus_grant ;
logic bus_request ;
logic slave_ready ;
logic data_ready ;
logic mem_read ;
logic mem_write ;
endinterface //main_bus
/********************************顶层网表********************************/
module top(
input logic clock ,
input logic rst_n ,
input logic test_mode
);
logic [15:0] program_address ;
logic [15:0] jump_address ;
logic [7:0] instruction ;
logic [7:0] next_instruction ;
logic [7:0] data_b ;
main_bus bus (.*);
processor procl (.*);
slave slave1 (.*);
instruction ir (.*);
test_generator test_gen (.*);
dual_port_ram ram (.*,.data_b(next_instruction));
endmodule
10.2.1 源代码声明次序
10.2.2 全局与局部接口定义
10.3 将接口用作模块端口
10.3.1 显式命名的接口端口
10.3.2 通用接口端口
10.3.3 综合指导
10.4 接口的实例化和连接
10.5 接口内部信号的引用
/********************************接口内部信号的引用********************************/
module slave(
//main_bus接口端口
main_bus bus
//其他端口
);
//内部信号
logic [15:0] slave_data ;
logic [15:0] slave_address ;
logic [15:0] operand_A ;
logic [15:0] operand_B ;
logic mem_select ;
logic read ;
logic write ;
assign bus.slvae_address = mem_select?slave_address :'z;
assign bus.data = bus_slave_ready?slave_Data:'z;
typedef enum logic [4:0] { RESET = 5'b00001,
START = 5'b00010,
REQ_DATA = 5'b00100,
EXECUTE = 5'b01000,
DONE = 5'b10000} state_t;
state_t curr_state ;
state_t next_state ;
always_ff @( posedge bus.clock or negedge bus.rst_n ) begin
if(!bus.rst_n)begin
curr_state <= RESET;
end
else begin
curr_state <= next_state;
end
end
always_comb begin
unique case(curr_state)
START:begin
if(!bus.slave_request)begin
bus.bus_request = 0;
next_state = state;
end
else begin
operand_A = bus.data ;
slave_address = bus.address ;
end
end
//其他状态译码
endcase
end
endmodule
10.6 接口的modport
/********************************选择在模块实例中使用哪种modport********************************/
interface chip_bus (
input logic clock ,
input logic rst_n
);
modport master(...);
modport slave(...);
endinterface
module primary (interface pins);//通用接口端口
...
endmodule
module secondary (chip_bus pins);//特定接口端口
...
endmodule
module chip(
input logic clock ,
input logic rst_n
);
chip_bus bus (clock,rst_n); //接口实例
primary i1 (bus.master); //使用master modport方式
secondary i2 (bus.slave); //使用slave modport方式
endmodule
/********************************选择在模块实例中使用哪种modport********************************/
interface chip_bus (
input logic clock ,
input logic rst_n
);
modport master(...);
modport slave(...);
endinterface
module primary (chip_bus.master pins);//通用接口端口
...
endmodule
module secondary (chip_bus.slave pins);//特定接口端口
...
endmodule
module chip(
input logic clock ,
input logic rst_n
);
chip_bus bus (clock,rst_n); //接口实例
primary i1 (bus); //使用master modport方式
secondary i2 (bus); //使用slave modport方式
endmodule
10.6.2 使用modport定义不同的连接
可以层次化连接端口
10.7 在接口中使用任务和函数
10.7.1 接口的方法
可以封装连接模块的数据,也可以用来封装模块间的通信协议
10.7.2 接口方法的导入(import)
/********************************使用modport选择接口内的可选方法********************************/
/********************************接口定义********************************/
interface math_bus(
input logic clock ,
input logic rst_n
);
int a_int ;
int b_int ;
int result_int ;
real a_real ;
real b_real ;
real result_real ;
...
task IntergeRead(
output int a_int,
output int b_int
);
...//进行握手来提取a和b的值
endtask
task FloatingPointRead(
output real a_real ,
output real b_real
);
...//进行握手来提取a和b的值
endtask
modport int_io (
import IntegerRead ,
input clock ,
input rst_n ,
output result_int
);
modport fp_i0 (
import FloatingPointRead ,
input clock ,
input rst_n ,
output result_real
);
endinterface
/********************************顶层网表********************************/
module dual_mu(
input logic clock ,
input logic rst_n
);
math_bus bus_a;
math_bus bus_b;
interger_math_unit i1 (bus_a.int_io);
floating_point_unit i2 (bus_b.fp_io);
endmodule
/********************************模块定义********************************/
module integer_math_unit(interface io);
int a_reg,b_reg;
always_ff @( posedge io.clock ) begin
io.IntergeRead(a_reg,b_reg);//调用接口中的方法
..//进行算术运算
end
endmodule
module floating_point_unit( interface io);
real a_reg,b_reg;
always_ff @( posedge io.clock ) begin
io.FloatingPointRead(a_reg,b_reg);
..
end
endmodule
10.7.3 接口方法的综合原则
导入的函数和任务必须声明为自动类型,并且不能包含静态声明,这样才是可综合的。
10.7.4 导出(export)任务和函数
10.8 接口中的过程块
10.9 可重构接口
/********************************在模块中使用参数********************************/
interface math_bus #(
parameter type DTYPE = int
)(
input logic clock
);
DTYPE a,b,result;//参数化的类型
...
task Read(
output DTYPE a,b
);
...
endtask
modport int_io(
import Read ,
input clock ,
output result
);
modport fp_io(
import Read ,
input clock ,
output result
);
endinterface
module top (
input logic clock ,
input logic rst_n
);
math_bus bus_a(clock);
math_bus (#.DTYPE(real)) bus_b(clock);
integer_math_unit i1 (bus_a.int_io);
floating_point_unit i2(bus_b.fp_io);
endmodule
10.10 接口验证
十一、一个完整的建模
11.1 ATM实例
本章中用作例子的设计,是基于来自加尼克·伯杰验证行业协会(Janick Bergeron’s Verification Guild)的一个例子。原始的例子是用Verilog(遵循Verilog-1995标准)编写的不可综合的行为级模型。这个例子描述了一个用户与网络之间的四阶异步传输模式ATM的前向接口。在本书中,这个例子做了三项重大修改。第一代码使用了许多SystemVerilog的结构加以重写;第二原先的不可综合的行为级模型通过使用SystemVerilog的可综合子集加以重写;第三,模型修改为可配置的,这样可以很容易的把一个4X4的四阶开关扩展成 16X16 的开关或任何其他想要的配置。 本章中的例子说明了使用SystemVerilog的结构体联合体和数组是如何简化复杂的设计描述的。接口和接口方法的使用,则进一步简化了设计模块间复杂的数据通信。在这个例子中使用的SystemVerilog编码风格,也说明了如何在同一个源代码中自动的设置位数和配置。使用+define命令行选择项,设计的体系结构可以配置成一个NXP端口的前向节点,其中N和P可以是任意的正整数。原先的例子中,使用Verilog-1995只描述了一个固定的4X4设计。这个SystemVerilog的版本可以生成一个128X12816X128128X16或者其他任何可能的配置。模块和数据声明的大小设置和实例化是隐含处理的(包括这个例子中相对简单的测试平台)
11.2 数据抽象
这个ATM设计使用了两种ATM格式:UNI格式和NNI格式。
一个ATM单元只包含53字节的数据,这在Verilog中可以描述为一个字节数组。但是以这种方式建模时,单元中的这些字节的含义就会丢失。在SystemVerilog中,可以很容易的将这两种不同的格式定义为压缩结构体,并使单元的各个成员容易识别。
//UNI结构单元
typedef struct packed {
logic [ 3:0] GFC ;
logic [ 7:0] VPI ;
logic [15:0] VCI ;
logic CLP ;
logic [ 2:0] PT ;
logic [ 7:0] HEC ;
logic [0:47][ 7:0] Payload ;
} uniType;
//NNI结构单元
typedef struct packed {
logic [11:0] VPI ;
logic [15:0] VCI ;
logic CLP ;
logic [ 2:0] PT ;
logic [ 7:0] HEC ;
logic [0:47][ 7:0] Payload ;
} nniType;
//测试视图单元格式(payload部分)
typedef struct packed{
logic [0:4 ][7:0] Header ;
logic [0:3 ][7:0] PortID ;
logic [0:3 ][7:0] PacketID;
logic [0:39][7:0] Padding;
}tstType;
//UNI/NNI测试试图/字节流的联合体
typedef union packed {
uniType uni ;
nniType nni ;
tstType tst ;
logic [0:52][7:0] Mem ;
}ATMCellType;
现在,这53字节的数据可以很容易的以四种不同的方式配置:
UNI单元
NNI单元
测试平台中带标签的信息包
53字节的数据数组
11.3 接口封装
十二、行为级和交易级建模
本章定义属于行为及建模范畴得交易级建模(transaction level modeling,TLM)。包含以下主题:
交易的定义
总线的交易级模型
多个从模块
多个主模块间的仲裁
旗语(semaphore)
寄存器传输级模型和交易级模型接口
12.1 行为建模
12.2 什么是交易
SystemVerilog的交易级建模
行为级建模提高了模块功能的抽象层次,而交易级建模则通过隐藏接口间控制和数据流的细节,提高了模块和子系统间通信的抽象层次。