zl程序教程

您现在的位置是:首页 >  其他

当前栏目

FPGA基础自学流程

流程基础 FPGA 自学
2023-09-11 14:20:39 时间

目录

一、Quartus II与 Modelsim 软件安装与破解

二、完成基础电路仿真

1、组合逻辑电路

(1)、基本的与或非门电路仿真与基本的加减乘除仿真

(2)、半加器和全加器

(3)、编码器 和 译码器

(5)、数据选择器(二选一)

2、时序逻辑电路

 (1)、寄存器 和 移位寄存器

(2)、计数器(十进制 / 二十四进制 / 六十进制)

(3)、分频器(二分频 / 十分频)

3、状态机


一、Quartus II与 Modelsim 软件安装与破解

(备注:如果芯片是EP2XXX那么对应编程使用Cyclone II或以下版本,该版本只在Quartus13.0及以下版本支持使用,而EP4XXX及以上可适用Quartus13.0及以上版本。)

Quartus13.0安装包链接:

http://链接:https://pan.baidu.com/s/1TvCEyduApTvVlgcFJO9h_g    提取码:6666.

Quartus13.0安装教程链接:

https://blog.csdn.net/pang9998/article/details/83447190

Quartus18.0安装包及教程链接:

https://mp.weixin.qq.com/s/m1sFpNMR79V0Mk81axJ22Q

二、完成基础电路仿真

1、组合逻辑电路

(1)、基本的与或非门电路仿真与基本的加减乘除仿真

电路原件图(加减乘除不在其中):

代码:

//与门
module door(
	input in1, 
	input in2,
	output out
);
	//连续赋值(逻辑描述)
	assign out = in1 & in2;
	
	//位置调用(结构描述)
//	and (out, in1, in2);
endmodule	

//或门
module door(
	input in1, 
	input in2,
	output out
);
	//连续赋值(逻辑描述)
	assign out = in1 | in2;
	
	//位置调用(结构描述)
//	or (out, in1, in2);
endmodule	

//非门
module door(
	input in, 
	output out
);
	//连续赋值(逻辑描述)
	assign out = ~ in;
	
	//位置调用(结构描述)
//	not (out, in1);
endmodule	

//除此之外还有“nor”异或 “xnor”同或

//加减乘除
module door(
	input in1, 
	input in2,
	output out
);
	assign out = in1 + in2; //1+1=0,1+0=1
	assign out = in1 - in2;	//0-1=1,1-1=0
	assign out = in1 * in2; //0*1=0,1*1=1
	assign out = in1 / in2; //1/1=1,当in1=1,in2=0和in1=0,in2=0时出现1/2
endmodule	

(2)、半加器和全加器

设计思路参考链接:

https://wuzhikai.blog.csdn.net/article/details/124116237

  • 半加器

半加器是一种将两个一位二进制相加,并对应输出“结果”和“进位”的加法器运算电路。

主代码:

//半加器
module add(
	input in1,  //加数1
	input in2,	//加数2
	output sum, //两个数的加和
	output cout //加数和的进位
);
//第一种方式
	assign sum = in1 ^ in2;
	assign cout = in1 & in2;
//第二种方式
	assign {cout, sum} = in1 + in2;
	
endmodule

Testbench仿真代码:

`timescale 1ns/1ns
module sim_add();

//输入reg定义
reg in1, in2;
//输出wire定义
wire cout, sum;

//给初始信号
initial begin
	in1<=1'b0;					
	in2<=1'b0;					
end

//↓写成令每10ns,产生一次随机输入
always #10 in1<= {$random} %2;	//每10个时钟周期产生1个随机数
always #10 in2<= {$random} %2;

//创建模块
add adder(
	.in1(in1),
	.in2(in2),
	.sum(sum),	
	.cout(cout)
);
endmodule

其仿真电路图(从左到右分别是第一种和第二种):

 ModelSim仿真波形:

全加器是一种可以对两个一位二进制数据进行相加,并输出“结果”、“进位”和“低位进位”的一种器件。

主代码:

module add(
	input in1,  //加数1
	input in2,	//加数2
	input cin,  //低位向高位的进位
	output sum, //两个数的加和
	output cout //加数和的进位
);
	assign {cout, sum} = in1 + in2 + cin;

endmodule

Testbench仿真代码:

`timescale 1ns/1ns
module sim_add();

//输入reg定义
reg in1, in2, cin;
//输出wire定义
wire cout, sum;

//给初始信号
initial begin
	in1<=1'b0;					
	in2<=1'b0;	
	cin<=1'b0;
end

//↓写成令每10ns,产生一次随机输入
always #10 in1<= {$random} %2;	//每10个时钟周期产生1个随机数
always #10 in2<= {$random} %2;
always #10 cin<= {$random} %2;

//创建模块
add adder(
	.in1(in1),
	.in2(in2),
	.cin(cin),
	.sum(sum),	
	.cout(cout)
);
endmodule

仿真电路图:

ModelSim仿真波形:

 

(3)、编码器 和 译码器

  • 编码器

编码器是一种可以将少位输入信号转换为特定多位编码的器件。

8-3编码器设计参考链接:

https://blog.csdn.net/Ding_ding_fly/article/details/54882615?ops_request_misc=&request_id=&biz_id=102&utm_term=8-3%E7%BC%96%E7%A0%81%E5%99%A8&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-54882615.142^v47^pc_rank_34_1,201^v3^control_2&spm=1018.2226.3001.4187

主代码:

//8-3编码器
module encode_decode(
	input      [7:0]in,  
	output reg [2:0]out 
);
	
always @(*) begin
	case (in)
			8'b00000001: out = 3'b000;
			8'b00000010: out = 3'b001;
			8'b00000100: out = 3'b010;
			8'b00001000: out = 3'b011;
			
			8'b00010000: out = 3'b100;
			8'b00100000: out = 3'b101;
			8'b01000000: out = 3'b110;
			8'b10000000: out = 3'b111;
	endcase
end
endmodule

Testbench仿真代码:

`timescale 1ns/1ns
module sim_encode_decode();

//输入reg定义
reg  [7:0]in;
//输出wire定义
wire [2:0]out;

//给初始信号
initial begin
	in[0] = 1;in[1] = 0;in[2] = 0;in[3] = 0;
	in[4] = 0;in[5] = 0;in[6] = 0;in[7] = 0;
	#100;
	in= in << 1;
	#100;
	in= in << 1;
	#100;
	in= in << 1;
end					


//创建模块
encode_decode encode(
	.in(in[7:0]),
	.out(out[2:0])
);
endmodule

仿真电路图(部分展示):

ModelSim仿真波形:

  • 译码器(包含3-8译码器和数码管译码器)

译码器是一种可以将少位输入信号转换为特定多位编码的器件。

设计参考链接:

https://blog.csdn.net/quanqueen/article/details/113094663

>3-8译码器主代码:

//3-8译码器
module encode_decode(
	input      [2:0]in,  
	output reg [7:0]out 
);
	
always @(*) begin
	case (in)
			3'b000: out = 8'b00000001;
			3'b001: out = 8'b00000010;
			3'b010: out = 8'b00000100;
			3'b011: out = 8'b00001000;
			
			3'b100: out = 8'b00010000;
			3'b101: out = 8'b00100000;
			3'b110: out = 8'b01000000;
			3'b111: out = 8'b10000000;
	endcase
end
endmodule

Testbench仿真代码:

`timescale 1ns/1ns
module sim_encode_decode();

//输入reg定义
reg  [2:0]in;
//输出wire定义
wire [7:0]out;

//给初始信号
initial in<=3'b0;					

//↓写成令每10ns,产生一次随机输入
always #10 in<= {$random} %2;	

//创建模块
encode_decode decode(
	.in(in[2:0]),
	.out(out[7:0])
);

endmodule

仿真电路图:

ModelSim仿真波形:

数码管译码器

数码管译码器是一种转门对于数码管显示的译码器。

设计参考链接:

https://blog.csdn.net/weixin_63090979/article/details/121106854

>数码管译码器主代码:

//数码管编码器(共阳极)
module ni_tub_dec(
	input      [3:0]in,  
	output reg [7:0]out 
);
	
always @(*) begin
	case (in)
			4'b0000: out = 8'b1100_0000;
			4'b0001: out = 8'b1111_1001;
			4'b0010: out = 8'b1010_0100;
			4'b0011: out = 8'b1011_0000;
			
			4'b0100: out = 8'b1001_1001;
			4'b0101: out = 8'b1000_0010;
			4'b0110: out = 8'b1100_0000;
			4'b0111: out = 8'b1111_1000;
			4'b1000: out = 8'b1000_0000;
			4'b1001: out = 8'b1001_0000;
	endcase
end
endmodule

Testbench仿真代码:

`timescale 1ns/1ns
module sim_ni_tub_dec();

//输入reg定义
reg  [3:0]in;
//输出wire定义
wire [7:0]out;

//给初始信号
initial begin
		in = 4'd0;
		#100;
		in = 4'd1;
		#100;
		in = 4'd2;
		#100;
		in = 4'd3;
		#100;
		in = 4'd4;
		#100;
		in = 4'd5;
		#100;
		in = 4'd6;
		#100;
		in = 4'd7;
		#100;
		in = 4'd8;
		#100;
		in = 4'd9;
		#100;
	$stop;
end					

//创建模块
ni_tub_dec decode(
	.in(in[3:0]),
	.out(out[7:0])
);
endmodule

仿真电路图:

ModelSim仿真波形:

(5)、数据选择器(二选一)

数据选择器是一种根据选择位输出的相应信号的器件。

设计参考链接:

https://blog.csdn.net/hyhop150/article/details/51222711

主代码:

//二选一数据选择器
module dat_sel(
	input   in1,
	input   in2,
	input   sel,
	output  out 
);
	
	assign out = sel? in1:in2 ; 
	
endmodule

Testbench仿真代码:

`timescale 1ns/1ns
module sim_dat_sel();

//输入reg定义
reg   in1, in2, sel;
//输出wire定义
wire  out;

//给初始信号
initial begin
		in1 <= 0;in2 <= 1;sel <= 0;
		#100
		in1 <= 1;in2 <= 0;sel <= 0;
		#100
		in1 <= 0;in2 <= 1;sel <= 1;
		#100
		in1 <= 1;in2 <= 0;sel <= 1;
		#100
	$stop;
end					

//创建模块
dat_sel selector(
	.in1(in1),
	.in2(in2),
	.sel(sel),
	.out(out)
);
endmodule

仿真电路图:

ModelSim仿真波形:

2、时序逻辑电路

 (1)、寄存器 和 移位寄存器

  • 寄存器

由时钟边沿触发的一种用于寄存数据的器件。

 寄存器设计参考链接:

https://blog.csdn.net/cjx_csdn/article/details/105203494

主代码:

//基本寄存器
module rag_srag(
	input   in,
	input   clk,
	output  reg out 
);

	always @(posedge clk) begin
		out = in;
	end
	
endmodule

Testbench仿真代码:

//基本寄存器
`timescale 1ns/1ns
module sim_rag_srag();

//输入reg定义
reg   clk, in;
//输出wire定义
wire  out;

//给初始信号
initial begin
		in <= 0;clk <= 1;
		#50
		in <= 1;clk <= 1;
		#50
		in <= 0;clk <= 0;
		#50
		in <= 1;clk <= 0;
		#50
	$stop;
end					

//创建模块
rag_srag rag(
	.in(in),
	.clk(clk),
	.out(out)
);

endmodule

仿真电路图:

ModelSim仿真波形:

  • 移位寄存器

由时钟边沿触发,且会将寄存数据左移右移的一种寄存器。

设计参考链接:

https://reborn.blog.csdn.net/article/details/80377919

主代码:

//移位寄存器(右)
module rag_srag(
	input   clk,
	input   [15:0]in,
	output  reg [15:0]out 
);

	always @(posedge clk) begin
		out = {in[0], in[15:1]};
	end
	
endmodule

Testbench仿真代码:

//移位寄存器(16位)
`timescale 1ns/1ns
module sim_rag_srag();

//输入reg定义
reg   clk;
reg   [15:0]in;
//输出wire定义
wire  [15:0]out;

always #50 clk = ~clk;

//给初始信号
initial begin
		in =16'b0;clk = 1;
		#50
		in =16'b0000_0000_0000_0001;
		#50
		in =16'b1100_0000_0000_0011;
		#50
		in =16'b0011_0000_0000_1111;
		#50
		in =16'b0011_0000_0011_1111;
		#50
		in =16'b0011_0110_0000_1000;
		#50
	$stop;
end					

//创建模块
rag_srag srag(
	.in(in[15:0]),
	.clk(clk),
	.out(out[15:0])
);

endmodule

仿真电路图:

ModelSim仿真波形:

(2)、计数器(十进制 / 二十四进制 / 六十进制)

主要通过时钟的边沿信号触发计数的一种器件。

设计参考链接:

https://blog.csdn.net/weixin_43758368/article/details/101517017

主代码:

//计数器(四位十进制)
//无输入,内定一个寄存器进行时钟上升沿计数,最后通过输出反映出来
module counter(
	input   clk,rst,  //时钟,复位
	output  [3:0]out
);
	reg  [3:0]q;
	
	assign out = q;
	
	always @(posedge clk) 
	begin
		if(rst==0)
			q = 0;
		else if(q>=4'd9) 
			q = 0;
		else 
			q = q + 1;
	end
	
endmodule 

//计数器(五位二十四进制)
module counter(
	input   clk,rst,  //时钟,复位
	output  [4:0]out
);
	reg  [4:0]q;
	
	assign out = q;
	
	always @(posedge clk) 
	begin
		if(rst==0)
			q = 0;
		else if(q>=5'd24) 
			q = 0;
		else 
			q = q + 1;
	end
	
endmodule

//计数器(五位二十四进制)
module counter(
	input   clk,rst,  //时钟,复位
	output  [5:0]out
);
	reg  [5:0]q;
	
	assign out = q;
	
	always @(posedge clk) 
	begin
		if(rst==0)
			q = 0;
		else if(q>=6'd60) 
			q = 0;
		else 
			q = q + 1;
	end
	
endmodule

Testbench仿真代码:

//计数器(十进制)
`timescale 1ns/1ns
module sim_counter();

//十进制定义
reg   clk, rst;
wire  [3:0]out;

//二十四进制定义
reg   clk, rst;
wire  [4:0]out;

//六十进制定义
reg   clk, rst;
wire  [5:0]out;

always #5 clk <= ~clk;

//给初始信号
initial begin
		clk <= 0;rst <= 0;
		#10;
		rst <= 1;
end					

//十进制模块
counter coun10(
	.rst(rst),
	.clk(clk),
	.out(out[3:0])
);
//二十四进制模块
counter coun24(
	.rst(rst),
	.clk(clk),
	.out(out[4:0])
);
//六十进制模块
counter coun60(
	.rst(rst),
	.clk(clk),
	.out(out[5:0])
);

endmodule

仿真电路图:

(十进制)

(二十四进制)

(六十进制)

 ModelSim仿真波形(例六十进制):

(3)、分频器(二分频 / 十分频)

用于通过原有的高时钟频率信号,生成低时钟频率信号的器件。

设计参考链接:

https://blog.csdn.net/supenman_mwg/article/details/7654141

主代码(二分频 / 十分频):

//二分频
//使用复位仿照时钟由边沿改变输出信号,配合着原本的时钟,这就形成了二分的频率
module fre_div(
	input   clk,rst,  //时钟,复位
	output  out_clk
);

reg  q;
assign out_clk = q;
	
always @(posedge clk, posedge rst) 
	begin
		if(rst==1)
			q = 0;
		else 
			q = ~q;
	end
	
endmodule

//十分频
//需要定义一个计数器,计五个二分频
module fre_div(
	input   clk,rst,  //时钟,复位 
	output  out_clk
);
parameter N = 5;
reg [N-1:0]cnt;  //计数器
reg  q;				
assign out_clk = q;
	
always @(posedge clk, posedge rst) begin
		if(rst==1) begin
			q <= 0;
			cnt <= 0;
		 end
		else begin
			if(cnt>=N-1)begin 
				 q <= ~q;
				 cnt <= 0;
			  end
			else begin 
				 cnt <= cnt + 1;
			  end
		 end
	end
endmodule

Testbench仿真代码:

//二分频/十分频
`timescale 1ns/1ns
module sim_fre_div();

reg   clk, rst;
wire  out_clk;

always #20 clk <= ~clk;

//给初始信号
initial begin
		clk=0;rst=1;
      #24 rst =0;
end					

fre_div divider2(
	.rst(rst),
	.clk(clk),
	.out_clk(out_clk)
);

endmodule

仿真电路图:

(二分频)

(十分频)

ModelSim仿真波形:

(二分频)

(十分频)

3、状态机

状态机,全称是有限状态机,是一种在有限个状态之间按一定规律转换的时序电路,可以认为是组合逻辑和时序逻辑的一种组合。
➢ Mealy状态机:组合逻辑的输出不仅取决于当前状态,还取决于输入状态。
➢ Moore状态机:组合逻辑的输出只取决于当前状态。
 

 状态机设计参考链接:

https://wuzhikai.blog.csdn.net/article/details/119421783

主代码:

/**********<三段式状态机>*************/
//特点:在二段的基础上,再次细分,将输出的状态分离,形成划分明确的状态机
//------"摩尔型"-----//
//三段米勒型写法,同上段与摩尔的关系写法一样
module sta_mac(
	input      clk,   //时钟信号
	input      rst,	//复位信号(低电平有效)
	input      money,	//投币信号(高电平有效)
	output reg cola	//可乐信号(高电平为出可乐)
);
//独热码定义(状态参数)
localparam	Init     = 4'b0001,
				One_cin  = 4'b0010,
				Two_cin  = 4'b0100,
				There_cin= 4'b1000;

reg [3:0] curr_sta;   //现态
reg [3:0] next_sta;   //次态(相当于一个中间传递数据的寄存器)

//***第一段:同步时序"状态转移"
always @(posedge clk, negedge rst) begin
	if(!rst)
		curr_sta <= Init;		  //回到初始
	else
		curr_sta <= next_sta;  //向现态传递次态数据
end

//***第二段:组合逻辑状态"转移条件"
always @(*) begin
		case (curr_sta) // 依据现态情况转移状态
			Init: begin             //初始状态			
				if(money)				
					next_sta <= One_cin;
				else 
					next_sta <= Init;
			end
			One_cin: begin				//一个硬币状态			
				if(money)				
					next_sta <= Two_cin;
				else 
					next_sta <= One_cin;
			end
			Two_cin: begin				//二个硬币状态		
				if(money)
					next_sta <= There_cin;
				else 
					next_sta <= Two_cin;
			end
			There_cin: begin			//三个硬币状态		
				if(money)				
					next_sta <= One_cin;
				else 
					next_sta <= Init;
			end
			default: begin				//其他情况,同初始状态
				if(money)				
					next_sta <= One_cin;
				else 
					next_sta <= Init;
			end
		endcase
end

//***第三段:各个"状态对应的输出情况"
always @(posedge clk, negedge rst) begin
	if(!rst)
		cola <= 1'b0;
	else 
		case (curr_sta)
			Init:      cola <= 1'b0;
			One_cin:   cola <= 1'b0;
			Two_cin:   cola <= 1'b0;
			There_cin: cola <= 1'b1;
			default:   cola <= 1'b0;
		endcase
end
endmodule

Testbench仿真代码:

//状态机仿真代码
`timescale 1ns/1ns
module sim_sta_mac();
reg	clk;   		//时钟信号
reg	rst;			//复位信号
reg	money; 		//投币信号
wire	cola;	 		//可乐信号

//建立模块
sta_mac machin_1duan(
	.money(money),
	.rst(rst),
	.clk(clk),
	.cola(cola)
);

//给初始信号
initial begin
	clk = 1'b0;			//初始时钟为0
	rst <= 1'b0;		//初始复位
	money <= 1'b0;				//投币初始化为0
	#5								

	rst <= 1'b1;		//拉高复位,系统进入工作状态
	#25							
	money <= 1'b1;				//拉高投币信号(投币)	
	#40							
	money <= 1'b0;	    		//拉低投币信号(不投币)
	#20							
	money <= 1'b1;	    		//拉高投币信号(投币)
	#80							
	money <= 1'b0;				//拉低投币信号(不投币)	
end					
//给时钟信号
always #10 clk <= ~clk;

//状态名称查看器(看不了)
/*reg [71:0] state_name;
always @(*) begin
	case (machin_1duan.state)
		4'b0001: state_name = "Init";
		4'b0010: state_name = "One_cin";
		4'b0100: state_name = "Two_cin";
		4'b1000: state_name = "There_cin";
		default: state_name = "Init";
	endcase
end*/

endmodule

状态图:

仿真电路图:

ModelSim仿真波形:

四、最后一步,也就是上FPGA板做仿真实验

上板实验进阶可以于B站搜索:野火FPGA