主要内容来自《设计与验证:verilog hdl》1这本书。

语法


操作符

  • 连接m,n - {m, n}
  • m重复n次 - {n{m}}

Module

`timescale 1ns / 1ps
module sample #(
  parameter WIDTH=10,
  parameter PARAM=1
  ) (
  input [WIDTH-1:0] in,
  output [WIDTH-1:0] out
  );
endmodule

这里的#()写法只适用于Vivado。

case语句

// casez 将z看做不关心的值
casez (encoder)
  4'b1??? : out = 3;
  4'b01?? : out = 2;
  4'b001? : out = 1;
  4'b0001 : out = 0;
  default : out = 0;
endcase
// casex 将x和z都看做不关心的值
casex (encoder)
  4'b1xxx : out = 3;
  4'b01xx : out = 2;
  4'b001x : out = 1;
  4'b0001 : out = 0;
  default : out = 0;
endcase

循环语句

  • forever 无限循环
  • repeat(n) 重复n次
  • while(true) while循环
  • integer i;for(i = 1;i < 7; i=i+1) for循环

一些技巧

// 赋值、延时
{out3,out2,out1,out0} = #4 'b0001;
assign #3 result = (sel) ? A : B;
// 寄存器
output res_out;
wire res;
always @ (posedge clk or negedge rst_n)
  if (~rst_n)
    res_out <= 0;
  else
    res_out <= res;

信号类型

包括1 0 z x。比如,case语句中的default,其实表示的包括10zx各种其他情况

避免锁存器

锁存器有产生竞争冒险的风险。

// 避免锁存器
always @(sel or a or b or c)
begin
  if (sel == 2'b00)
    q = a;
  else if(sel == 2'b01)
    q = b;
  else if(sel == 2'b10)
    q = c;
  else
    q = 1'bx; // sel == 2'b11的时候q值不关心,否则q=q会产生锁存器
end

当然,使用触发器,即@使用边沿触发的时候,是没有问题的。

always @(posedge clk or negedge rst_n)
begin
  if (~rst_n)
    q <= 0;
  else if(en)
    q <= a + b;
  // 否则q<=q,即触发器
end

建模

并行与串行

begin...end是顺序执行的,fork...join是并行执行的。

即使在begin...end里,非阻塞赋值<=也是并行执行的。

使用caseif...else if...else if语句建模出来是并行执行的,使用多if语句if... if... if...建模的结构是有优先级的,但是现在的综合工具通常会把不必要的优先级结构优化掉。

阻塞赋值与非阻塞赋值

阻塞赋值顺序执行,非阻塞赋值在最后一起执行。

建议always敏感表边沿触发全部使用非阻塞赋值,电平触发全部使用阻塞赋值。

存储器建模

reg [datawidth] MemoryName [addresswidth];
// 8bit位宽,地址64位RAM8x64
reg [7:0] RAM8x64 [0:63];
// 对于存储器,只能整体读写
reg mem_data = RAM8x64[addr];
RAM8x64[addr+1] = mem_data;

一般都是直接使用FPGA内部的RAM,不需要手动建模

复位

异步复位、同步释放

always @(posedge clk)
  rst_reg <= rst_n;
always @(posedge clk or negedge rst_reg)
  if (!rst_reg)
    begin
      ...
    end
  else
    begin
      ...
    end

避免在复位释放的时候刚好时钟边沿到达造成亚稳态。

仿真与调试

输入输出

// 打印
$display ("At time %t - ", $time, " something");

// 文件输入输出
//   输出
integer Write_Out_File; // 文件指针
Write_Out_File = $fopen("Write_Out_File.txt");
$fdisplay(Write_Out_File, "@%h\n%h", Mpi_addr, Data_in);
$fclose(Write_Out_File);
//   输入
reg [7:0] DataSource [0:47];
$readmemh("Read_In_File.txt", DataSource);
// $readmemb
// Read_In_File.txt:
//   @2f
//   24
//   @2e
//   91

延时

// 延时的写法
// min:typ:max表示最小、典型和最大延时,顺序为上升延时、下降延时、关闭延时以及输出变成X的延时。未定义的延时取已定义的延时中的最小值
// 注意:assign中的延时通常是被逻辑综合工具忽略的,仅仅用于仿真
assign #(2:3:4, 3:4:5, 1, 2) a = in1 ^ in2;

Testbench写法

// initial forever写法
reg clk=0;
initial begin
  forever #10 clk<=~clk;
end
// always 写法
always
  #10 clk<=~clk;
end
// 仿真退出
$finish
// 仿真挂起
$stop

Vivado License1

下载或者自建一个lic文件

INCREMENT VIVADO_HLS xilinxd 2037.05 permanent uncounted AF3E86892AA2 \
    VENDOR_STRING=License_Type:Bought HOSTID=ANY ISSUER="Xilinx \
    Inc" START=19-May-2016 TS_OK
INCREMENT Vivado_System_Edition xilinxd 2037.05 permanent uncounted \
    A1074C37F742 VENDOR_STRING=License_Type:Bought HOSTID=ANY \
    ISSUER="Xilinx Inc" START=19-May-2016 TS_OK
PACKAGE Vivado_System_Edition xilinxd 2037.05 DFF4A65E0A68 \
    COMPONENTS="ISIM ChipScopePro_SIOTK PlanAhead ChipscopePro XPS \
    ISE HLS_Synthesis AccelDSP Vivado Rodin_Synthesis \
    Rodin_Implementation Rodin_SystemBuilder \
    PartialReconfiguration AUTOESL_FLOW AUTOESL_CC AUTOESL_OPT \
    AUTOESL_SC AUTOESL_XILINX petalinux_arch_ppc \
    petalinux_arch_microblaze petalinux_arch_zynq ap_sdsoc SDK \
    SysGen Simulation Implementation Analyzer HLS Synthesis \
    VIVADO_HLS" OPTIONS=SUITE


# 2037年之前的任何Vivado版本(包括HLS、AccelDSP、System Generator、软硬CPU、SOC、嵌入式Linux、重配置等等功能)都是永久使用。使用本license文件时要改名,文件名不能有汉字和空格。
  1. 《设计与验证:Verilog HDL》 吴继华 人民邮电出版社  2