目录
一、Quartus II与 Modelsim 软件安装与破解
一、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编码器设计参考链接:
主代码:
//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