玩命加载中 . . .

异步fifo代码

module afifo
#(parameter DATA_WIDTH = 64,
            FIFO_DEPTH = 8,
            AF_LEVEL   = (1<<FIFO_DEPTH - 10),
            AE_LEVEL   = 10
)
(
  input                         wclk          ,
  input                         wrst          ,
  input                         wen           ,
  input       [DATA_WIDTH-1:0]  wdata         ,
  output reg                    wfull         ,
  output reg                    walmost_full  ,

  input                         rclk          ,
  input                         rrst          ,
  input                         ren           ,
  output reg  [DATA_WIDTH-1:0]  rdata         ,
  output reg                    rempty        ,
  output reg                    ralmost_empty
);


wire                  fifo_rd;
wire                  fifo_wr;
wire [FIFO_DEPTH-1:0] raddr;
wire [FIFO_DEPTH-1:0] waddr;
reg  [DATA_WIDTH-1:0] memory [0:(1<<FIFO_DEPTH)-1];

wire                  wen_mask;
wire [FIFO_DEPTH:0]   wbinnext;
wire [FIFO_DEPTH:0]   wptrnext;
reg  [FIFO_DEPTH:0]   wbin;
reg  [FIFO_DEPTH:0]   wptr;
reg  [FIFO_DEPTH:0]   wptr_rsync1;
reg  [FIFO_DEPTH:0]   wptr_rsync2;
wire [FIFO_DEPTH:0]   wbin_rsync;

wire                  ren_mask;
wire [FIFO_DEPTH:0]   rbinnext;
wire [FIFO_DEPTH:0]   rptrnext;
reg  [FIFO_DEPTH:0]   rbin;
reg  [FIFO_DEPTH:0]   rptr;
reg  [FIFO_DEPTH:0]   rptr_wsync1;
reg  [FIFO_DEPTH:0]   rptr_wsync2;
wire [FIFO_DEPTH:0]   rbin_wsync;

genvar i;

//*************************************
// Dual port RAM
//*************************************
assign fifo_wr = wen_mask;
always @(posedge wclk) begin
  if(fifo_wr)
    memory[waddr] <= wdata;
end

assign fifo_rd = ren_mask;
always @(posedge rclk) begin
  if(fifo_rd)
    rdata <= memory[raddr];
end
//

//*************************************
// sync wptr to rclk domain
//*************************************
always @(posedge rclk or negedge rrst) begin
  if(rrst)
    {wptr_rsync2,wptr_rsync1} <= 0;
  else
    {wptr_rsync2,wptr_rsync1} <= {wptr_rsync1,wptr};
end

generate
  for(i=FIFO_DEPTH;i>=0;i=i-1) begin: wbin_rsync_gen
    assign wbin_rsync[i] = ^({ {i{1'b0}},wptr_rsync2[FIFO_DEPTH:i]});
  end
endgenerate

//*************************************
// sync rptr to wclk domain
//*************************************
always @(posedge wclk or negedge wrst) begin
  if(wrst)
    {rptr_wsync2,rptr_wsync1} <= 0;
  else
    {rptr_wsync2,rptr_wsync1} <= {rptr_wsync1,rptr};
end

generate
  for(i=FIFO_DEPTH;i>=0;i=i-1) begin: rbin_wsync_gen
    assign rbin_wsync[i] = ^({ {i{1'b0}},rptr_wsync2[FIFO_DEPTH:i]});
  end
endgenerate

//*************************************
// rd addr generate
//*************************************
always @(posedge rclk or negedge rrst) begin
  if(rrst)
    {rbin,rptr} <= 0;
  else
    {rbin,rptr} <= {rbinnext,rptrnext};
end
assign raddr    = rbin[FIFO_DEPTH-1:0];
assign ren_mask = ren & (~rempty);
assign rbinnext = ren_mask ? (rbin + 1'b1) : rbin;
assign rptrnext = (rbinnext>>1) ^ rbinnext;


//*************************************
// wr addr generate
//*************************************
always @(posedge wclk or negedge wrst) begin
  if(wrst)
    {wbin,wptr} <= 0;
  else
    {wbin,wptr} <= {wbinnext,wptrnext};
end
assign waddr = wbin[FIFO_DEPTH-1:0];
assign wen_mask = (wen & (~wfull));
assign wbinnext = wen_mask ? (wbin + 1'b1) : wbin;
assign wptrnext = (wbinnext>>1) ^ wbinnext;

//*************************************
// FIFO status
//*************************************
always @(posedge rclk or negedge rrst) begin
  if(rrst)
    rempty <= 1'b1;
  else
    rempty <= (rptrnext == wptr_rsync2);
end

always @(posedge rclk or negedge rrst) begin
  if(rrst)
    ralmost_empty <= 1'b1;
  else
    ralmost_empty <= (wbin_rsync - rbinnext <= AE_LEVEL);
end

always @(posedge wclk or negedge wrst) begin
  if(wrst)
    wfull <= 0;
  else
    wfull <= (wptrnext == {~rptr_wsync2[FIFO_DEPTH:FIFO_DEPTH-1],rptr_wsync2[FIFO_DEPTH-2:0]});
end

always @(posedge wclk or negedge wrst) begin
  if(wrst)
    walmost_full <= 1'b1;
  else
    walmost_full <= (wbinnext - rbin_wsync >= AF_LEVEL);
end

endmodule

小环境makefile模板

design = tb
verilog_flist = -f ./test.vc tb.v
vhd_flist =
wave = wave.fsdb

#whether to open verdi wave gui
ifdef wave
	verdi_cmd += -ssf $(wave)
endif

.PHONY: help clean ius compile verdi

help:
	@echo "#==============================================================="
	@echo "# [Info] make help    : show help infomation"   
	@echo "# [Info] make clean   : clean ius and verdi compile library,log"   
	@echo "# [Info] make compile : compile for verdi"   
	@echo "# [Info] make verdi   : raise verdi gui"   
	@echo "#==============================================================="

clean:
	@rm chip_lib.lib++ work.lib++ INCA_libs verdiLog vericomLog vhdlcomLog -rf

ius:
	irun +access+wr -smartorder -clean -ntcnotchks -V93 -vtimescale 1ns/1ps +define+RD=1 -work chip_lib $(verilog_flist) $(vhd_flist) -top $(design) -64bit

compile:
ifdef vhdl_flist
	vhdlcom -V93 -sup_sem_error -smartorder -work chip_lib $(vhdl_flist)
endif
ifdef verilog_flist
	vericom -sv +systemverilog+sv +v95ext+v95 +verilog2001ext+v -ignorekwd_config +define+RD=1  -work chip_lib $(verilog_flist)
endif

verdi:compile
	verdi -lib chip_lib $(verdi_cmd) &

force verilog内部信号

force verilog内部信号为某个值,可以这样写:

initial begin  //verilog internal signal access
  force xxx.xxx.xxx = 16'hfffd;
  #500 release xxx.xxx.xxx;
end

force vhdl内部信号

而对于vhdl内部信号,使用force是错误的,vhdl语言不允许这样做。但是EDA工具一般都会提供函数,例如cadence的ncsim,可以使用$nc_forcenc_release访问vhdl内部信号。

initial begin //vhdl internal signal access
  $nc_force("xxx.xxx.xxx", "X'FFFD'");
  #500 $nc_release(xxx.xxx.xxx);
end
  • 类封装了数据和操作这些数据的子程序
  • 类可以定义在program、package、module中,或者在这些块之外的任何地方。类应当在program或者module之外的package中定义,避免与其他块内变量出现冲突。
  • new()创建对象、分配空间并执行构造函数
  • 赋值为null释放对象空间
  • 通过static来声明静态变量,是的该变量被该类的所有实例所共享,但使用范围仅限于该类
  • 通过类名::变量来访问静态变量
  • 静态变量在声明时初始化
  • 静态方法跟静态变量类似,也通过static声明
  • 静态方法不允许读写非静态变量
  • 类中的方法默认是自动存储的
  • 当使用一个变量时,会优先在当前作用域中寻找,接着在上一级作用域寻找,直到找到该变量为止。this可以明确指定当前作用域。
  • 接口信号必须使用非阻塞赋值来驱动
  • modport将信号分组并指定方向
  • clocking时钟块用于控制同步信号的时序

testbench注意事项:

  • $exit用于结束程序块,$finish用于结束仿真
  • 时钟产生不应该放在program程序块中,会引起信号的竞争,而应该放在module中
  • 例化时如果端口名字和数据类型一致,例化时可以用.*(隐式端口连接)

DUT代码

以简单的乘法器为例写testbench

//mul.v
module mul(
  input             clk,
  input             rst_n,
  input             in_vld,
  input      [11:0] mul_ain,
  input      [11:0] mul_bin,
  output reg [22:0] mul_out,
  output reg        out_vld
);

always @(posedge clk)
  if(in_vld)
    mul_out <= $signed(mul_ain) * $signed(mul_bin);

always @(posedge clk,negedge rst_n)
  if(!rst_n)
    out_vld <= 1'b0;
  else
    out_vld <= in_vld;

endmodule
  • 缺省情况下参数的类型是与其前一个参数相同的,而第一个参数的缺省类型是logic单bit输入
  • 参数的传递方式可以使用ref指定为引用,通常用于数组引用;当不希望子程序改变数组值时,可以使用const ref类型
  • ref参数只能被用于带自动存储(automatic)的子程序中,即使用ref参数时子程序必须指明automatic属性
  • 自动存储相对的是静态存储,当多个地方调用静态存储子程序时,不同的线程之间会窜用这些局部变量,而自动存储能够迫使仿真器使用堆栈存储局部变量
  • 函数使用return返回一个值,调用时可以使用void忽略返回值,例如void'($fscanf(file,"%d",i));
module test_func();

int arr[];

//************************************
// operator = "+", return a.sum
// operator = "x", return a.product
//************************************
function automatic int alu(
    const ref int a[],           //数组引用,const不能改变数组
    input string operator = "+"  //指定default值
);

int result;

if(operator=="x") begin
  result = 1;
  for(a[i])
    result *= a[i];
end
else begin
  result = 0;
  for(a[i])
    result += a[i];
end

return result;
endfunction

initial begin
  arr = new[5];
  arr = '{1,2,3,4,5};
  $display(alu(arr));    //15, use default operator
  $display(alu(arr,"x")) //120
end

endmodule

内建数据类型

四状态:reg, wire, logic, integer, time(默认值为X)
双状态:bit, int, byte, shortint, longint, real(默认值为0)
有符号数:int, byte, shortint, longint, integer(可以使用unsigned申明为无符号数)

🔖 $isunknown操作符:
作用:使用$isunknown操作符,可以在表达式的任意位出现X或Z时返回1

//test_isunknown.sv
module test_isunknown();

logic [3:0] din;

initial begin
  din = 4'b1001;  //Unknown not found!
  din = 4'b1x01;  //Unknown is detected!
  din = 4'b1z01;  //Unknown is detected!
  if($isunknown(din))
    $display("Unknown is detected!");
  else
    $display("Unknown not found!");
end

endmodule

cadence irun命令

常用编译

irun +access+wr -smartorder -clean -ntcnotchks -V93 -vtimescale 1ns/1ps -work chip_lib -f xxx.vc -top tb_top -64

coverage选项

-covtest xxx -coverage all -covoverwrite -covfile covfile.ccf -covworkdir ./cov_work
其中covfile.ccf定义coverage内容

set_expr_scoring -all
set_fsm_scoring -hold_tansition
set_libcell_scoring
set_implicit_block_scoring -off
set_covergroup -per_instance_default_one

select_coverage -all -instance tb_top.xxx*...