赞
踩
关于Verilog
verilog
- 以文本形式来描述数字系统硬件的结构和行为的语言。
- 表示逻辑电路图、逻辑表达式、数字逻辑系统所完成的逻辑功能
- 五个层次:系统级、算法级、寄存器传输级、门级、开关级
Verilog的设计方法
自上向下(top-down)
先定义顶层模块的功能 再分成子模块
常用:wire和reg
wire in_1;
reg clk;
- 向量:位宽大于1
支持指定bit位后固定位宽的向量域选择访问
- `[bit+:width]`
- `[bit-:width]`
- 对信号进行重新组合时需要借助大括号
```verilog
wire [31:0] temp1,temp2;
assign temp1 = {byte[0][7:0],data1[31:8]};
<数组名>[<下标>]
比如integer tmp[7:0];
8个整数组成的数组操作数有一位为x或z时,结果为x
归约操作符
只有一个操作数,对这个向量操作数逐位进行操作,最终产生1bit的结果
A = 4'b1010;
&A; //result: 1 & 0 & 1 & 0 可用来判断变量A是否全为1
~|A; //re:~(1|0|1|0) 可用来判断变量A是否全为0
移位操作符
算术左移和逻辑左移时,右边低位会补 0。
逻辑右移时,左边高位会补 0;而算术右移时,左边高位会补充符号位,以保证数据缩小后值的正确性。
以反引号开始的某些标识符时verilog的系统编译指令
\\`define 用于文本替换
`define DATA_DW 32
\\`undef 用于取消之前的宏定义
`undef DATA_DW
\\`ifdef `elsif `else `endif
\\`include 编译时将一个verilog文件内嵌到另一个verilog文件中
\\`timescale 将时间单位与实际时间相关联,格式为
`timescale time_unit/time_precision
\\时间精度必须小于等于时间单位,影响仿真时占用的内存
\\`default_nettype 用于为隐式的线网变量指定为线网类型
\\`resetall 将所有的编译指令重新设置成缺省值
\\`celldefine `endcelldefine 用于将模块标记为单元模块
\\`unconnected_drive `nounconnected_drive 出现在这两个编译指令之间的任何未连接的输入端口,为正偏电路状态或者为反偏电路状态
assign
wire cout,a,b;
assign cout = a & b;
实例:全加器
module full_adder(
input Ai,Bi,Ci,
output So,Co
);
assign So = Ai ^ Bi ^ Ci;
assign Co = (Ai & Bi) | (Ci & (Ai | Bi));
endmodule
\*
可以写成
assign {Co,So} = Ai + Bi + Ci;
*\
仿真在vivado
中可以不锁引脚,其仿真如下
`timescale 1ns/1ns //设置时间单位和精度 module test; reg Ai,Bi,Ci; wire So,Co; //输出值一般都是wire类型 initial begin //初始化 {Ai,Bi,Ci} = 3'b0; forever begin //每10ns加一 #10; {Ai,Bi,Ci} = {Ai,Bi,Ci} + 1'b1; end end //实例化 full_adder u_adder( .Ai (Ai), .Bi (Bi), .Ci (Ci), .So (So), .Co (Co) ); //暂时未知 待补 initial begin forever begin #100; if($time >= 1000) begin $finish; end end end endmodule
行为级建模的两种基本语句:initial
和always
一个模块中可以包含多个initial
和always
语句,但2种语句不能嵌套使用
initial
语句从0时刻开始执行,只执行一次,多用于初始化、信号检测
always
重复执行,多用于仿真时钟的产生、信号行为的检测等
//产生100MHz时钟 `timescale 1ns/1ns module test; parameter CLK_FREQ = 100; //100MHz parameter CLK_CYCLE = 1e9 / (CLK_FREQ * 1e6); //转换成ns reg clk; initial clk = 1'b0; always # (CLK_CYCLE/2) clk = ~clk; always begin #10 if($time >= 1000) begin $finish end end endmodule
阻塞赋值
、非阻塞赋值
initial
里使用\\ 阻塞赋值和非阻塞赋值的区别 `timescale 1ns/1ns module test; reg[3:0] ai,bi; reg[3:0] ai2,bi2; reg[3:0] val_blk; reg[3:0] val_non; reg[3:0] val_non_2; initial begin ai = 4'd1; bi = 4'd2; ai2 = 4'd7; bi2 = 4'd8; #20; ai = 4'd3; bi = 4'd4; val_blk = ai + bi; val_non <= ai + bi; ai2 <= 4'd5; bi2 <= 4'd6; val_non_2 = ai2 + bi2; end always begin #10; if($time >= 1000) $finish; end endmodule
常常在时序逻辑块中用非阻塞,组合逻辑中用阻塞,使用非阻塞赋值避免竞争冒险
过程连续赋值
能够替换其他所有wire
和reg
的赋值,发生作用时,其右端表达式中任何操作数变化都会引起过程连续赋值语句重新执行
assign
和deassign
module dff_normal(
input rstn,
input clk,
input D,
output reg Q
);
always @(posedge clk or negedge rstn) begin
if(!rstn) begin
Q <= 1'b0;
end
else begin
Q <= D;
end
end
endmodule
用assign
和deassign
改写
module dff_assgin( input rstn, input clk, input D, output reg Q ); always @(posedge clk) begin Q <= D; end always @(negedge rstn) begin if(!rstn) begin assign Q = 1'b0; end else begin deassign Q; end end endmodule
force
和release
两大时序控制方法:时延控制 事件控制
时延控制
指定了语句从开始执行到执行完毕的时间间隔,时延可以是数字、标识符或者表达式
reg val_test;
reg val_general;
#10 val_general = val_test;
//或直接写为 #10;
- 内嵌时延
```verilog
\\遇到内嵌时延时,需要先将计算结果保存
reg val_test;
reg val_embed;
val_embed = #10 val_test;
边沿触发事件控制
在Verilog中,事件指某一reg或wire变量发生值的变化
posedge
正跳变negedge
负跳变event start_receiving;
always @(posedge clk_samp) begin
-> start_receiving;
end
always @(start_receiving) begin
data_buf = {data_if[0],data_if[1]};
end
- 敏感列表
当多个信号或事件中任意一个发生变化时都能触发语句执行,这些事件组成的列表叫敏感列表。
```verilog
always @(*)
/*
always @(posedge clk or negedge rstn)
*/
电平敏感事件控制
电平是敏感信号,常用while来表示电平敏感情况
initial begin
wait(start_enable);
forever begin
@(posedge clk_samp);
data_buf = {data_if[0],data_if[1]};
end
end
顺序块和并行块:提供了将两条或更多条语句组成语法结构上相当于一条一句的机制。
顺序块: begin
和 end
并行块: fork
和 join
`timescale 1ns/1ns module test; reg[3:0] ai_sequen,bi_sequen; reg[3:0] ai_paral,bi_paral; reg[3:0] ai_nonblk,bi_nonblk; initial begin #5 ai_sequen = 4'd5; #5 bi_sequen = 4'd8; end initial fork #5 ai_paral = 4'd5; #5 bi_paral = 4'd8; join initial fork #5 ai_nonblk <= 4'd5; #5 bi_nonblk <= 4'd8; join endmodule
顺序块和并行块可以嵌套着用
命名块 可以给块语句结构命名,声明局部变量,通过层次名引用的方法对变量进行访问
`timescale 1ns/1ns module test; initial begin: module_name //命名模块必须要加分号 integer i; i = 0; forever begin #10 i = i + 10; end end reg stop_flag; initial stop_flag = 1'b0; always begin: detect_stop if(test.module_name.i == 100) begin stop_flag = 1'b1; end #10; end
命名的块也可以被禁用,用disable
表示,可以用来从循环中退出或处理错误。
`timescale 1ns/1ns module test; initial begin: module1 integer i; i = 0; if(i >= 50) begin disable module2.clk_gen; disable module1; end i = i + 10; end reg clk; initial begin: module2 while(1) begin: clk_gen clk = 1; #10; clk = 0; #10; end end
当disable
在always
和forever
中使用时仅跳出当前回合,下一回合仍继续执行。
if 条件表达式必须在圆括号里
if(condition) // do while the condition is true
else if(condition_2) //TO-DO
else //do while all conditions are fault
case 多路条件分支
case(case_express)
condition_1: //to-do
condition_2: //to-do
default: //to-do
endcase
/* 表示条件选项中的无关项
casex x表示无关值
casez ?表示无关值
*/
while
while(condition) begin
//to-do
end
for
for(initial_assignment;condition;step_assignment) begin
//to-do tips:i++ and i-- is not admitted in verilog
end
repeat
repeat(times) begin
end
forever
forever begin //equal to while(1)
// use $finish to exit it
end
![[Pasted image 20221218231709.png]]
module
开始,endmodule
结束input
、output
和inout
三种类型。在一个模块中引用另一个模块,对其端口进行相关连接
命名端口连接
将需要例化的模块端口与外部信号按照其名字进行连接
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c == 1'b1 ? 1'b0 : 1'b1),
.So (so_bit0),
.Co (co_temp[0]) //.Co () 悬空,output一般可以删除
);
顺序端口连接
将需要例化的模块端口按照模块声明时端口的顺序与外部信号进行匹配连接,位置要严格保持一致
full_adder1 u_adder0(
a[1],b[1],co_temp[0],so_bit0,co_temp[1]
);
端口例化注意事项
genvar i;
generate
//TO-DO
endgenerate
带参数例化
当一个模块被另一个模块引用例化时,高层模块可以对低层模块的参数值进行改写。这样就允许在编译时将不同的参数传递给多个相同名字的模块,而不用单独为只有参数不同的多个模块再新建文件。
defparam
层次访问
通过使用一连串的.
对各模块进行层次分隔连接,多用于仿真中。
a = top.u_m2.u_n3.c;//访问子模块u_m2中叶单元u_n3中的变量c
可以利用task
或function
将重复性的行为级设计进行封装
task
描述共同代码段,并在模块内任意位置被调用
函数功能 + 时序控制逻辑
特点
/*
声明格式:
task task_id:
// Port Declaration
// Procedural Statement
endtask
*/
function
/*
声明格式:
function [range-1:0] function_id;
input_declaration;
other_declaration;
procedural_statement;
endfunction
调用格式:
function_id(input1,input2,...);
*/
module digital_tube( input clk, input rstn, input en, input[3:0] single_digit, input[3:0] ten_digit, input[3:0] hundred_digit, input[3:0] kilo_digit, output reg[3:0] csn, output reg[6:0] abcdefg ); reg[1:0] scan_r; always @(posedge clk or negedge rstn) begin if(!rstn) begin csn <= 4'b1111; abcdefg <='d0; scan_r <= 3'd0; end else if(en) begin case(scan_r) 2'd0:begin scan_r <= 3'd1; csn <= 4'b0111; //选择个位 abcdefg <= dt_translate(single_digit); end end end /*--------translate function--------------*/ function [6:0] dt_translate; input[3:0] data; begin case(data) 4'd0:dt_translate = 7'b1111_110; //0 4'd1:dt_translate = 7'b0110_000; //1 4'd2:dt_translate = 7'b1101_101; //2 4'd3:dt_translate = 7'b1111_001; //3 4'd4:dt_translate = 7'b0110_011; //4 4'd5:dt_translate = 7'b1011_011; //5 4'd6:dt_translate = 7'b1011_111; //6 4'd7:dt_translate = 7'b1111_000; //7 4'd8:dt_translate = 7'b1111_111; //8 4'd9:dt_translate = 7'b1111_011; //9 end endfunction endmodule
有限状态机
Moore型:输出只与当前状态有关,和当前输入无关。输入、输出是隔离开的。
Mealy型:输出在输入发生变化后立刻改变
自动售货机
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。