Verilog中的module可以看成一个具有输入输出端口的黑盒子,该黑盒子有输入和输出接口(信号),通过把输入在盒子中执行某些操作来实现某项功能。(类似于C语言中的函数)
图1 模块示意图
图1 所示的顶层模块(top_module)结构用Verilog语言可描述为:
module top_module(
input a,
input b,
output out
);
.......
endmodule
同理,图1 所示的次级模块(mod_a)结构用Verilog语言可描述为:
module top_module(
input in1,
input in2,
output out
);
.......
endmodule
注意事项:每个模应单独块处于一个.v文件中,模块名即为文件名(规范代码!)
模块的输入输出端口都可看出模块的信号,若不写信号类型则默认为wire类型信号!
// 以下这两个语句本质是一直的
input a;
input wire a;
除了wire型信号,还有reg型信号,具体详见1.4节!
如图1所示,top_module的两个输入端口连接到次级模块(mod_a)的输入端口,那如何在top_module模块模块中使用mod_a模块的功能呢?这就需要通过模块实例化,可以把top_module看成C语言中的主函数,次级模块mod_a看成普通函数,这样就可以在主函数中调用其他函数来完成相应的功能!
在top_module中实例化mod_a的方式为:
模块实例化语法: 模块名 实例名(定义连接port的信号);
module top_module(
input a,
input b,
output out
);
mod_a instance2 (.in1(a), .in2(b), .out(out));
endmodule
always块可构建 组合逻辑块 和 时序逻辑块,复杂的逻辑操作都需要处于该逻辑块中,如if、case、for等
(1) 组合逻辑块
module top_module();
always @(*) begin
....
end
endmodule
(1) 时序逻辑电路
module top_module();
always @(posedge clk) begin
....
end
endmodule
generate主要结合for循环使用,主要用途有:
(1) 操作向量
module top_module(input [7:0] in, output [7:0] out);
genvar i; // genvar i; 也可以定义在generate内部
generate
for(i=0; i<8; i++) begin: bit
assign out[i]=^in[8-1:i];
end
endgenerate
endmodule
(2) 模块重复多次实例化
module top_module(
input a,
input b,
output out
);
genvar i;
generate
for(i=0; i<8; i++) begin: gen_mod_a // gen_mod_a 为每个begin_end的结构的名称
mod_a instance2 (.in1(a), .in2(b), .out(out));
end
endgenerate
endmodule
initial块可以理解为一个初始化块,在initial的起始位置的语句在0时刻即开始执行,之后如果遇到延时,则延时之后执行接下来的语句。
初始块是不可综合的,因此不能将其转化为带有数字元素的硬件原理图。因此初始块除了在仿真中使用外,并没有太大的作用。
如:在仿真文件中初始化各种参数:
initial
begin
sys_clk = 1'b1;
sys_rst_n = 1'b0;
#50
sys_rst_n = 1'b1;
end
注意:
若需要在RTL代码中初始化参数,需要用always块,用initial块会导致错误!
如下所示,在RTL代码中初始化存储器的方式为:
reg [255:0]char_data[4:0];
always@(posedge clk)
begin
char_data[0] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char_data[1] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char_data[2] <= 256'h0000000000000000000000200000000000000000002000000008000010000000;
char_data[3] <= 256'h000000000000000000000038000000000000020000380000000600001C000000;
char_data[4] <= 256'h02000000000100000000003E0000000000000700003E0000000780001F000000;
end
Verilog 中赋值方式有三种: 连续赋值、阻塞赋值、非阻塞赋值
assign x = y;
// 组合块
always @(*) begin
out1 = a ;
a = b ;
out2 = a ;
end
// 时序块
always @(posedge clk) begin
out1 <= a ;
a <= b ;
out2 <= a ;
end
(1)用途: 标识符用于定义常数、变量、信号、端口、参数名、模块名等。
(2)组成: 字母、数字、$、下划线任意组合而成
(3)注意事项:
Verilog中有4中逻辑值:0、1、x、z
注意:这里的z、x是不区分大小写的(X、Z也可)
(1) 逻辑运算符:&&(与)、==(相等)、||(或)、!=(不等)
(2) 按位运算符: &、|、~、^、~&、~^、~|
(3) 归约运算符: &、|、~、^、&、~^、~|
&a [3:0] // AND:a[3]&a[2]&a[1]&a [0]相当于(a[3:0]== 4'hf)
|b [3:0] // OR: b[3]|b[2]|b[1]|b [0]相当于(b[3:0]!= 4'h0)
^c [2:0] // XOR:c[2]^c[1]^c[0]
与C语言类似,常量主要有:整数型、实数型和字符串型三种
(1) 正数: 直接写 10 表示位宽为32bit的十进制整数(系统默认)
(2) 负数: -10需要用二进制补码表示,多了一位符号位(1 1010)
(3) 用科学计数法表示:12.345e3 表示 12345
[换算成二进制数后的位宽]'[数制符号][与数制对应的值]
(1) 二进制(b): 8'b1000_1100
(2) 十六进制(h): 8'h8c
(3) 八进制(o): 8'o214
(4) 十进制(d): 8'140
注意事项:
(1) 每个字符由1个8位的ASCII码值表示,即需要1byte存储空间
(2) 如:“Hello world” 字符串由11个ASCII符号构成,需要11byte存储空间
Verilog中注释主要有行注释(//)和块注释(/* .... */)两种,表示方法与C语言一致!
// 行注释
/*
块注释
*/
Verilog中的变量主要有两种: wire和reg
(1) 线网型(wire): 表示电路间的物理连接,wire定义的变量也可看成信号端口
(2) 当两个wire信号被连续赋值时,在逻辑块中会被映射成真实的物理连线,此时这两个信号端口的变化是同步的!
wire a;
wire b;
assign b = a; // 表示a与b之间生成实际的物理连线
(1) 寄存器型(reg): 表示一个抽象的数据存储单元
(2) reg 具有对某一时间点状态进行保持的功能
(1) 在always、initial语句中被赋值的变量(赋值号左边的变量)都是reg型变量
(2) 在assign语句中被赋值的变量,为wire型变量
(1) 参数是一种常量,通常出现在module内部,常被用于定义状态、数据位宽等
parameter STATE = 1'b0;
(2) 只作用于声明的那个文件,且可以被灵活改变!
(3) 局部参数localparam,只在本模块中使用
localparam STATE= 1'b1’;
(4) 参数的名称一般为大写,以区分其他变量
vector(向量),是一组信号的集合,可视为位宽超过1bit 的 wire 信号。
(1) 定义方式:
格式:input/output wire/reg [upper:lower] vector_name
//输入输出型
input [7:0] a,b,
output reg [7:0] out
// 模块中间向量
wire [7:0] c, e;
reg [7:0] d;
多路选择器应用:实现一个 256 选 1 选择器,sel 信号作为选择信号,当 sel = 0 时选择 in[3:0],sel = 1 时选择 in[7:4],以此类推。
module top_module (
input [1023:0] in,
input [7:0] sel,
output [3:0] out
);
assign out = {in[sel*4+3], in[sel*4+2], in[sel*4+1], in[sel*4+0]};
// assign out = in[sel*4 +: 4];
// assign out = in[sel*4+3 -: 4];
endmodule
参考文章:HDLBits:在线学习 Verilog (十三 · Problem 60-64) - 知乎 (zhihu.com)
(1) 与C语言相同,Verilog也有三元表达式:
condition ? if_true : if_false
当条件为真,表达式值为if_true ,否则表达式值为if_false。
(2) 应用
(sel ? b : a) // 一个二选一MUX,通过sel的值选择a或者b
always @(posedge clk) // 一个T触发器
q <= toggle ? ~q : q;
assign out = ena ? q : 1'bz; // 三态缓冲器
(3) 参考文章: HDLBits:在线学习Verilog(八 · Problem 35-39) - 知乎 (zhihu.com)
(1) 最常用的形式:(优势:输出的所有可能都写到,不存在未知电平输出!)
if(<条件表达式 1>)
语句或语句块 1;
else if(<条件表达式 2>)
语句或语句块 2;
………
else
语句或语句块 n;
(2) 不建议使用if-else嵌套,会存在优先级问题,导致逻辑混乱,
(3) 所有if-else语句都应写成(1)的形式!
(4) 根据条件表达式依次比较,存在优先级!
(1) 书写形式:
case(<控制表达式>)
<分支语句 1> : 语句块 1;
<分支语句 2> : 语句块 2;
<分支语句 3> : 语句块 3;
………
<分支语句 n> : 语句块 n;
default : 语句块 n+1;
endcase
比较<控制表达式>与<分支语句n>的取值相等则执行对应语句,否则执行default后语句!
(2) 执行完某一分支语句后立即跳出case语句结构,终止case语句执行。
(3) <分支语句n>的取值必须互不相同!
(4) 以encase结束case语句块
(5) 各分支语句间不存在优先级!
(6) 具体应用: 用case语句搭建多路选择器,(以9选1多路选择器为例)
module top_module(
input [15:0] a, b, c, d, e, f, g, h, i,
input [3:0] sel,
output [15:0] out );
always @(*) begin
case(sel)
4'h0:begin out = a; end
4'h1:begin out = b; end
4'h2:begin out = c; end
4'h3:begin out = d; end
4'h4:begin out = e; end
4'h5:begin out = f; end
4'h6:begin out = g; end
4'h7:begin out = h; end
4'h8:begin out = i; end
default: out = 16'hffff;
endcase
end
endmodule
(1) 书写形式:
integer i;
always @(*) begin
for(i=0; i<n; i++) begin: for_name
<循环语句>
end
end
用一对花括号加逗号组成“{ , }”拼接运算符,逗号隔开的数据按顺序拼接成新数据!
wire [1:0] a;
wire [3:0] b;
wire [5:0] c;
wire [11:0] d = {a, b, c}
在左边拼接实现右移,右边拼接实现左移!
always @(posedge clk) begin
if(rst_n == 1'b0)
out <= 4'b0;
else
out <= {in, out[3:1]}; // 右移
end
语法: {重复次数{vector}}
{3{a}} = {a, a, a}
{3'd5, {2{3'd6}}} // 9'b101_110_110.
移位运算符用于将左边操作数左移或右移指定的位数!移位后空闲位用0填充。
如: 4‘b1101 << 3 结果为:4‘b1000
如: 4‘b1101 >> 3 结果为:4‘b0001
代码实现:
module add1 (
input a,
input b,
input cin,
output sum,
output cout
);
assign sum = a^b^cin;
assign cout = (a&b) | (a&cin) | (b&cin);
endmodule
16进制全加器如上图所示,它可由上节中16个二进制全加器组合而成。
用Verilog实现16进制全加器代码为:
module add16 (
input [15:0] a,
input [15:0] b,
input cin,
output [15:0] sum,
output cout
);
wire [16:0] Add_cin;
assign Add_cin[0] = cin; // 上图中第一个二进制加法器进位输入为0 assign Add_cin[0] = 1b'0;
// 用 generate 进行模块多次实例化
// generate 应用范围:对矢量(vector)多个位重复操作,模块重复实例化
genvar i;
generate
for(i=0; i<16; i++) begin: gen_add16 // gen_add16 为每个begin_end的结构,仿真器会通过他来标识生成结构,gen_add16[0],gen_add16[1]....
add1 Add16(.a(a[i]), .b(b[i]), .cin(Add_cin[i]), .sum(sum[i]), .cout(Add_cin[i+1]));
end
endgenerate
assign cout = Add_cin[16];
endmodule
module counter
// 参数传递
#(
parameter COUNT_MAX = 25'd24_999_999,
parameter STATE = 1'b0 // 多个参数用逗号隔开
)
(
input wire sys_clk,
output reg led_out
);
// 代码主体
endmodule
// 参数传递
#(
.COUNT_NUM( 25'd24_999_999), // 传入参数
.STATE(1'b0)
)
counter1_init // 实例化模块的名称位置
(
.sys_clk (sys_clk),
.led_out(led_out)
);
参考资料:
[1] 野火《FPGA Verilog开发实战指南》:[野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开发板 — [野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开发板 文档 (embedfire.com)https://doc.embedfire.com/fpga/altera/ep4ce10_pro/zh/latest/index.html
[2] HDLBits中文导学:HDLBits 中文导学 - 知乎 (zhihu.com)
文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr
文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc
文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8
文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束
文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求
文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname
文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立
文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码
文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词
文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限
文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定
文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland