FPGA学习笔记(2)——Verilog语法及ModelSim使用

1.1 语法

1、赋值语句 = 和 <=

= 为阻塞赋值,当该语句结束时,下一个语句才开始执行,串行执行

<= 为非阻塞幅值,该语句和整个语句块同时执行,并行执行

1.2 ModelSim使用

1、修改源文件路径:File -> Source Directory/Change Directory

2、查看窗口:View -> Transcript/Project/Library

3、新建工程:File->New->Project ,添加文件夹名称

4、添加源文件和TB文件:Project 右键-> Add to Project. Existing File

5、编译:Project 右键 -> Compile All

如果出现黄色三角形的警告,查看相关文件并修改,重新编译

双击红色的警告,可以看到.v文件哪里出现问题

6、仿真:Simulate -> Start Simulate -> Design -> work

  • 选择TB文件,关闭Enable optimization

7、Libraries、SDF(标准延迟文件)

8、添加信号到波形图内:sim-Default 右键添加Add Wave

某些信号看不到(no data)就重新仿真,最好先添加信号,再进行仿真。

修改Run Length 到适合的时长(例如1us),run运行设置时长

左侧Restart,重置信号。

9、命令行操作:

.main clear 清空命令行

run 1us 波形运行1us

10、ctrl+A:全选信号

ctrl+G:信号排序

1.3 Verilog

1、逻辑值:

  • 0
  • 1
  • X(未知,可能是1/0)
  • Z(高阻态,外部没有激励信号,是一个悬空状态)

2、进制:二进制(d)、八进制(o)、十进制(d)和十六进制(h)

3、数值表示:[数据位宽]'[进制][数]

例子:1'b0 8'd255 16b'1001_1010_0000_1111(下划线不影响程序读取)

4、标识符:定义模块名、端口名、信号名

任意一组字母、数字、$符号和_(下划线)

标识符第一个字符必须是字母或下划线,区分大小写。

不建议大小写混合使用,普通信号建议全部小写,信号命名最好体现信号的含义,简洁清晰易懂

例子:sum、cpu_addr、clk_50、clk_cpu

5、数据类型:寄存器、线网、参数,前两个是真正在数字电路中起作用的

(1)寄存器类型:reg,默认初始值为X

例子:

reg [31:0] delay_cnt;

reg key_reg;

注:reg类型只能在always和initial语句中被幅值。

时序逻辑:always语句带有时钟信号,则该寄存器变量对应为触发器;

组合逻辑:always语句不带有时钟信号,则该寄存器变量对应为硬件连线;

(2)线网类型:表示结构实体之间的物理连线,此变量不能存储值,它的值由驱动它的元件所决定。

驱动线网类型变量的元件有门、连续幅值语句、assign。

如果没有驱动元件连接到线网上,线网为高阻态z。

例子:

wire key_flag;

(3)参数类型:常量,用parameter定义常量。

例子:

parameter H_SYNC = 11'd41; //行同步

parameter H_BACK = 11'b2; //行显示后沿

parameter H_DISP = 11'd480; //行有效数据

parameter H_FRONT = 11'd2; //行显示前沿

parameter H_TOTAL = 11'd525; //行扫描周期

参数型数据常用于定义状态机的状态、数据的位宽和延迟大小。

标识符、参数传递

6、运算符:算数运算符(+ - * / %)、关系运算符(> < >= <= == !=)、逻辑运算符(! && ||)、

条件运算符(? : ,例子:a?b:c,a为真,选择b,否则选择c)、位运算符(~ & | ^)、

移位运算符(<< >> ,例子:8'b11110000 >>2 = 2'b00111100,0填充)、拼接运算符({})

有优先级,用括号!

7、注释方式: // 和 /* */

8、关键字

变量名不能与关键字同名

9、框架:

(1)模块block(包括接口和逻辑功能)

例子:

c 复制代码
module block(a,b,c,d);
	input a,b;
	output c,d;
	
	assign c = a | b;
	assign d = a & b;
endmodule

每个verilog程序包括:端口定义、IO说明、内部信号声明、功能定义。

注意:有可以综合的语句和不可综合的语句(仿真)

(2)可综合的语句:

assign、always、例化实例元件,这三种逻辑功能是并行的。

(2-1)在always块中,逻辑是顺序执行的。

而多个always块之间是并行的。

(2-2)模块调用:信号通过模块端口在模块之间传递。

例子:

文件seg_led_static_top.v:

c 复制代码
module seg_led_static_top(
	input		sys_clk,
	input		sys_rst_n,
	
	output	[5:0]	sel,
	output	[7:0]	seg_led
);
	parameter	TIME_SHOW = 25'd25000_000;
	wire		add_flag;
	//模块调用1:
	time_count #(
		.MAX_NUM	(TIME_SHOW)		//参数传递
	) u_time_count(
		.clk		(sys_clk),
		.rst_n		(sys_rst_n),
		.flag		(add_flag)
	);
	//模块调用2:必须按照模块定义顺序列写(不推荐)
	time_count #(
		.MAX_NUM	(TIME_SHOW)		//参数传递
	) u_time_count(
		sys_clk,
		sys_rst_n,
		add_flag
	);
endmodule

其他文件 time_cout.v:

c 复制代码
module time_count(
	input		clk,
	input		rst_n,
	output	reg	flag
);

parameter	MAX_NUM = 50000_00;
reg	[24:0]	cnt;

10、结构语句:

(1)initial:在模块中只执行一次。常用来写测试文件,产生仿真测试信号(激励信号)和对存储器赋初始值。

例子:

c 复制代码
initial begin
	sys_clk		<= 1'b0;
	sys_rst_n 	<= 1'b0;
	touch_key 	<= 1'b0;
	#20 sys_rst_n <= 1'b0;
end

(2)always:不断重复活动,但是只有和一定的时间控制结合在一起才有作用。

例子:

c 复制代码
always #10 sys_clk = ~sys_clk;

always的时间控制有:边沿触发,电平触发

可以是单个信号,也可以是多个信号(用or连接)

例子:

c 复制代码
always @(posedge sys_clk or negedge sys_rst_n) begin		//敏感列表
	if (!sys_rst_n)
		counter <= 24'd0;
	else if(counter < 24'd1000_0000)
		counter <= counter + 1'b1;
	else
		counter <= 24'd0;
end

边沿触发(posedge,negedge)的always常常描述时序逻辑行为。使用非阻塞幅值<=

电平触达的always常常描述组合逻辑行为。使用阻塞幅值=

例子:

c 复制代码
always @(a or b or c or d or e) begin
	out = a ?(b+c):(d + e);
end

可以用*代表所有变量,@(*)对后面语句块所有输入变量的变化都是敏感的!

c 复制代码
always @(*) begin
	out = a ?(b+c):(d + e);
end

组合逻辑没有CLK信号,时序逻辑有CLK信号,具备记忆功能。

注意:

(1)不允许在多各always块中对同一个变量进行幅值

(2)在同一个always块中不要既用非阻塞幅值又用阻塞赋值

11、条件语句:

(1)if

c 复制代码
if (a > b)
	out = data_1;

(2)if else

c 复制代码
if (a > b)
	out = data_1;
else
	out = data_2;

(3)if else嵌套:

c 复制代码
if (fx1)
	out = data_1;
else if(fx2)
	out = data_2;
else if(fx3)
	out = data_3;
else
	out = data_4;

(4)使用begin和end包含多个语句:

c 复制代码
if (a) begin
	语句1;
	语句2;
end
else begin
	语句1;
	语句2;
end

判断表达式的值:若为0,x,z,按照假进行处理,若为1,按照真处理。

(5)case语句:

casez:比较时,不考虑表达式中的高阻态z。

casex:比较时,不考虑高阻态z和不定值x

例子:

c 复制代码
case(num)
	4'h0	:	seg_led <= 8'b1111_0000;
	4'h1	:	seg_led <= 8'b0000_0000;
	default	:	seg_led <= 8'b1111_1111;
endcase

注意:num和n'hx必须位宽相等。

c 复制代码
casex(sel)
	8'b1100_zzzz	:	语句1;
	8'b1100_xxzz	:	语句2;
endcase

1.4 状态机

例子:利用FPGA实现电子门锁。

序列检测器

1、状态机(FSM):在有限个状态之间按一定规律转换的时序电路

2、模型:

(1)mealy状态机:输出与输入信号和当前状态有关。

组合逻辑F->状态寄存器->组合逻辑G

(2)moore状态机:输出只与当前状态有关。

3、状态机设计:

(1)步骤:状态空间定义,状态跳转,下个状态判断,各个状态下的动作。

例子:

c 复制代码
/*   part1:状态空间定义    */
//define state space
parameter	SLEEP	=	2'b00;
parameter	STUDY	=	2'b01;
parameter	EAT		=	2'b10;
parameter	AMUSE	=	2'b11;
//internal variable
reg	[1:0]	current_state;
reg	[1:0]	next_state;

//独热码:每个状态只有一个寄存器置位,译码逻辑简单,生成的电路简单。
parameter	SLEEP	=	4'b0001;
parameter	STUDY	=	4'b0010;
parameter	EAT		=	4'b0100;
parameter	AMUSE	=	4'b1000;
//internal variable
reg	[3:0]	current_state;
reg	[3:0]	next_state;

/*   part2:状态跳转   */
//transition
always @(posedge clk or negedge rst_n) begin		//敏感列表:时钟信号以及复位信号边沿的组合
	if(!rst_n)
		current_state <= SLEEP;
	else
		current_state <= next_state;				//使用非阻塞赋值
end

/*   part3:下个状态判断(组合逻辑)   */
//next state decision
always @(current_state or input_signals) begin
	case (current_state)
		SLEEP	:begin
				if (clock_alarm)
					next_state = STUDY;
				else
					next_state = SLEEP;
		end
		STUDY	:begin
				if (lunch_time)
					next_state = EAT;
				else
					next_state = STUDY;
		end
		EAT		:begin
				if (lunch_time)
					next_state = EAT;
				else
					next_state = STUDY;
		end
		AMUSE	:begin
				if (lunch_time)
					next_state = EAT;
				else
					next_state = STUDY;
		end
		default:begin
				if (lunch_time)
					next_state = EAT;
				else
					next_state = STUDY;
		end
	endcase
end

注意:

(1)组合逻辑使用阻塞赋值

(2)if/else要配对以免产生latch(锁存器),case的状态如果没有给完全,必须要给default,否则也会生成latch

c 复制代码
/*   part4:各个状态下的动作(组合逻辑)   */
//action
wire read_book;
assign read_book = (current_state == STUDY) ? 1'b1 : 1'b0;

always @(current_state) begin
	if(current_state == STUDY)
		read_book = 1'b1;
	else
		read_book = 1'b0;
end

注意:组合逻辑使用阻塞赋值

一个三段式状态机例子:divider7_fsm.v

三段式可以在组合逻辑后再增加一级寄存器(时序逻辑,有clk信号输入)来实现时序逻辑输出:

(1)可以有效滤除组合逻辑输出的毛刺;

(2)可以有效地进行时序计算和约束;

(3)对总线形式的输出信号来说,容易使总线数据对齐,从而减小总线数据间的偏移,减小接收端数据采样出错的频率。

相关推荐
波音彬要多做22 分钟前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法
m0_748256781 小时前
WebGIS实战开源项目:智慧机场三维可视化(学习笔记)
笔记·学习·开源
南七澄江2 小时前
各种网站(学习资源及其他)
开发语言·网络·python·深度学习·学习·机器学习·ai
机智的叉烧8 小时前
前沿重器[57] | sigir24:大模型推荐系统的文本ID对齐学习
人工智能·学习·机器学习
量子-Alex9 小时前
【多模态聚类】用于无标记视频自监督学习的多模态聚类网络
学习·音视频·聚类
吉大一菜鸡9 小时前
FPGA学习(基于小梅哥Xilinx FPGA)学习笔记
笔记·学习·fpga开发
爱吃西瓜的小菜鸡12 小时前
【C语言】判断回文
c语言·学习·算法
小A15912 小时前
STM32完全学习——SPI接口的FLASH(DMA模式)
stm32·嵌入式硬件·学习
岁岁岁平安12 小时前
spring学习(spring-DI(字符串或对象引用注入、集合注入)(XML配置))
java·学习·spring·依赖注入·集合注入·基本数据类型注入·引用数据类型注入
武昌库里写JAVA12 小时前
Java成长之路(一)--SpringBoot基础学习--SpringBoot代码测试
java·开发语言·spring boot·学习·课程设计