主要内容来自《设计与验证: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
里,非阻塞赋值<=
也是并行执行的。
使用case
和if...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文件时要改名,文件名不能有汉字和空格。