systemverilog设计
21 May 2024 7299字 25分 次 systemverilog打赏作者 CC BY 4.0 (除特别声明或转载文章外)
1. 过程块
1.1 组合逻辑过程块
always_comb不需要指明敏感列表,使用阻塞赋值(=),如果过程内部写出锁存器,则会警告或报错
always_comb begin
if(!mode)
y = a + b;
else
y = a - b;
end
注意:always@(*)并没有组合逻辑的语义,即可能产生latch
注意:如果过程块要调用函数,最好用always_comb,因为always@(*)不会推断函数中读取的信号
1.2 时序逻辑过程块
always_ff,使用非阻塞赋值(<=),如果过程内部写出组合逻辑,则会报错
always_ff @ (posedge clk, negedge rst_n) begin
if(!rst_n)
q <= 0;
else
q <= d;
end
1.3 锁存逻辑过程块
always_latch,使用非阻塞赋值(<=),如果写成其他电路,则会警告或报错
always_latch
if(en)
q <= d;
2. 任务和函数
TODO
3. for语句
2种写法: 比如描述一个Reduced-xor电路,如下图
第1种写法
module reduced_xor_gen
#(parameter N=8)
(
input logic [N-1:0] a,
output logic y
)
logic [N-1:0] p;
assign p[0] = a[0];
generate
genvar i;
for(i=1; i<N; i=i+1)
assign p[i] = a[i] ^ p[i-1];
endgenerate
assign y = P[N-1];
endmodule
第1种写法也是verilog支持的写法
第2种写法
module reduced_xor_gen
#(parameter N=8)
(
input logic [N-1:0] a,
output logic y
)
always_comb
begin
logic tmp;
tmp = a[0];
for (int i=1; i<N; i=i+1)
tem = a[i] ^ tmp;
y = tmp;
end
endmodule
第2种写法只在sv中支持,i为声明的局部变量。当for循环启动时,就自动创建这个变量并初始化,当循环推出时,这个变量就被清楚
3. 数组,结构体和联合体
3.1 结构体
结构体定义
// IEEE 754 Binary 32 encoding
typedef struct packed {
logic sign;
logic[FLOAT32_EXP_WIDTH - 1:0] exponent;
logic[FLOAT32_SIG_WIDTH - 1:0] significand;
} float32_t;
使用
float32_t fop1;
assign fop1 = '{1'b1, 8'h12, 23'h111111};
assign s = fop1.sign;
assign exp = fop1.exponent;
assign sign = fop1.significand;
TODO
4. sv声明的位置
包含以下几个内容:package, $unit编译声明空间, 未命名块中的声明, timescale
4.1 package
package中可以包含的可综合的结构有:
- parameter和localparameter常量定义
- const常量定义?
- typedef用户定义类型
- automatic task和function定义(static类型的task和function不可综合)
- 从其他包中import语句?
- 操作数重载定义?
4.1.1 定义如下:
package definitions;
parameter VERSION = "1.1";
typedef enum {ADD, SUM, MUL} opcode_t;
typedef struct {
logic [31:0] a, b;
opcode_t opcode;
} instruction_t;
function automatic [31:0] mul (input [31:0] a, b);
return a * b;
endfunction
endpackage
注意:包中的参数不能重新定义(verilog中可以对parameter重新定义) 注意:包中定义的任务和函数必须声明为automatic,并且不能包含静态变量
4.1.2 引用包的内容
4种方式:
- 用范围解析操作符直接引用
logic definitions::instruction inst; - 将包中特定子项导入到模块或接口中
import definitions::instruction; logic instruction inst; - 用通配符导入包中的子项到模块或接口中
import definitions::*; logic instruction inst; always_comb begin case(inst.opcode) ADD: result = inst.a + inst.b; SUB: result = inst.a - inst.b; MUL: result = mul(inst.a * inst.b); endcase end - 将包中子项导入到$unit声明域中 由于不能在关键字module和模块端口定义之间加一个import语句,因此,对于模块端口,包名任须显示引用,例如:
module ALU
(input definitions::instruction_t inst,
input logic [31:0] result
)
import definitions::*;
logic instruction inst;
always_comb begin
case(inst.opcode)
ADD: result = inst.a + inst.b;
SUB: result = inst.a - inst.b;
MUL: result = mul(inst.a * inst.b);
endcase
end
将包中子项导入到$unit声明域中:
import definitions::*;
module ALU
(input instruction_t inst,
input logic [31:0] result
)
logic instruction inst;
always_comb begin
case(inst.opcode)
ADD: result = inst.a + inst.b;
SUB: result = inst.a - inst.b;
MUL: result = mul(inst.a * inst.b);
endcase
end
4.2 $unit编译声明空间
注意:当多个文件同时进行编译时,只有一个$unit域 当单个文件进行编译时,必须将包中子项导入到$unit域中(每个文件会产生一个$unit域) 当多个文件一起进行编译时,会发生包中子项多次导入的情况(非法),因此,需要使用条件编译 defines.pkg文件如下:
`ifndef __DEFINES_SVH
`define __DEFINES_SVH
package defines;
endpackage : defines
每个需要包中定义的设计都应该将
`include "defines.pkg"
放入文件的开始
4.3 未命名块中的声明
verilog允许在命名的begin…end块中声明局部变量
module chip (input clock);
integer i; // declaration at module level
always @(posedge clock)
begin: loop
// named block
integer i;
// local variable
for (i=0; i<=127; i=i+1) begin
...
end
end
endmodule
sv允许在未命名的begin…end块中声明局部变量
module chip (input clock);
integer i; // declaration at module level
always @(posedge clock)
begin
integer i;
// unnamed block
// local variable
for (i=0; i<=127; i=i+1) begin
...
end
end
endmodule
4.4 timescale
TODO
5. 层次化设计
6. 接口
6.1 接口的概念
6.2 接口声明
接口可以像模块那样拥有端口
这样,clock, resetN, test_mode不需要像分立端口那样传递给每个模块实例
例如:
interface main_bus (input logic clock, resetN, test_mode);
wire [15:0] data;
wire [15:0] addr;
......
endinterface
顶层网表如下:
module top(input logic clock, resetN, test_mode);
logic [15:0] pc;
logic [7:0] inst;
main_bus bus(
.clock(clock),
.resetN(resetN),
.testmode(test_mode)
);
processor proc1(
.bus(bus),
.pc(pc),
.inst(inst)
);
endmodule
6.3 将端口用作模块端口
6.3.1 显示命名的接口端口
模块端口可以显示的声明为特定接口类型。可以把接口名称当作端口类型使用。 例如:
interface chip_bus;
......
endinterface
module Cache(chip_bus pins,
input clock);
...
endmodule
6.3.2 通用接口端口
通用接口端用关键字interface定义端口类型,而不是使用特定接口类型的名称。例如:
module RAM( interface pins,
input clock);
...
endmodule
当模块实例化时,任何接口都可以连接到通用接口端口上。
同一个模块可以用不同的接口连接?例子?
6.3.3 综合指导
接口连接模块的两种方式都是可以综合的。
6.4 接口的实例化和连接
接口类型的端口如果没有连接是非法的 模块实例的input, output和inout类型的端口可以没有任何连接。对于接口类型的端口则不行。
接口的端口也可以定义为一个接口
6.5 接口内部信号的引用
例如
always @ (posedge bus.clock, negedge bus.resetN)
...
6.6 接口的modport
modport定义了,从模块的角度来看接口信号的端口方向 例如:
interface chip_bus (input logic clock, resetN);
logic valid, ready;
logic [31:0] addr;
logic [31:0] data;
modport master (input clock,
input resetN,
output valid,
input ready,
output addr,
output data);
modport slave ( input clock,
input resetN,
input valid,
output ready,
input addr,
input data);
modport定义中不包含向量位数和类型。
6.6.1 指定使用哪种modport方式
- 在模块实例的接口连接中说明
chip_bus bus; //接口实例化 primary i1 (bus.master) //使用master modport - 在模块定义的端口声明时说明
module secondary (chip_bus.slave pins)以上两种方式不能同时使用
即使接口定义了modport,模块连接到接口时仍然可以不指定modport。 但是,接口内部信号的端口方向只能在modport中定义。 如果没有在接口连接中指定modport,那么接口中所有的线网信号将默认是双向inout信号。
6.6.2 使用modport定义不同的连接
模块只可以直接访问在modport定义中列出来的信号。这使得接口中的某些信号可以完全地对某些模块隐藏。
6.7 在接口中使用任务和函数
6.7.1 接口的方法
6.7.2 接口的方法的导入
- 使用函数和任务的名称名称导入
modport in( import Read, import parity_gen, input clock, resetN); - 使用函数和任务的完整原型导入
modport in( import task Read(input [63:0] data, output [31:0] addr), import function parity_gen(input [63:0] data), input clock, resetN);使用从接口中导入的任务或函数的模块是可综合的。 导入的函数和任务必须声明为自动类型,并且不能包含静态声明,这样才是可综合的。
下面是一个完整的例子:
///
// Simple AHB Interface with parity methods
///
interface simple_ahb (
input logic hclk, // bus transfer clk
input logic hresetN // bus reset, active low
);
logic [31:0] haddr; // transfer start address
logic [32:0] hwdata; // data to slave, with parity bit
logic [32:0] hrdata; // data from slave, with parity bit
logic [ 2:0] hsize; // transfer size
logic hwrite; // 1 for write, 0 for read
logic hready; // 1 for transfer finished
function automatic logic parity_gen(logic [31:0] data);
return(^data); // calculate parity of data (odd parity)
endfunction
function automatic logic parity_chk(logic [31:0] data,
logic parity);
return (parity === ^data); // 1=OK, 0=parity error
endfunction
// master module port directions
modport master_ports (
output haddr, hwdata, hsize, hwrite, // to AHB slave
input hrdata, hready, // from AHB slave
input hclk, hresetN, // from chip level
import parity_gen, parity_check // function import
);
// slave module port directions
modport slave_ports (
output hrdata, hready, // to AHB master
input haddr, hwdata, hsize, hwrite, // from AHB master
input hclk, hresetN, // from chip level
import parity_check // function import
);
// slave module port directions
modport slave_ports_alt (
output hrdata, hready, // to AHB master
input haddr, hwdata, hsize, hwrite, // from AHB master
input hclk, hresetN, // from chip level
import function logic parity_chk(logic [31:0] data,
logic parity)
);
endinterface: simple_ahb
6.8 可重构接口
6.8.1 参数化接口
///
// Simple AHB Interface with pareterized bus widths
///
interface simple_ahb
#(parameter DWIDTH=32) // Data bus width
(
input logic hclk, // bus transfer clk
input logic hresetN // bus reset, active low
);
logic [31:0] haddr; // transfer start address
logic [DWIDTH-1:0] hwdata; // data sent to slave
logic [DWIDTH-1:0] hrdata; // return data from slave
logic [ 2:0] hsize; // transfer size
logic hwrite; // 1 for write, 0 for read
logic hready; // 1 for transfer finished
// master module port directions
modport master_ports (
output haddr, hwdata, hsize, hwrite, // to AHB slave
input hrdata, hready, // from AHB slave
input hclk, hresetN // from chip level
);
// slave module port directions
modport slave_ports (
output hrdata, hready, // to AHB master
input haddr, hwdata, hsize, hwrite, // from AHB master
input hclk, hresetN // from chip level
);
endinterface: simple_ahb
例化
simple_ahb #(.DWIDTH(64))
ahbl(.hclk,
.hresetN
) ;
6.8.2 使用generate块
TODO