小梅哥Xilinx FPGA教程 课程例题整理
课程视频 : https://www.bilibili.com/video/BV1va411c7Dz?p=1&vd_source=371c3c83b450d1d337bdf87db2243dc5
课程资料:http://www.corecourse.cn/forum.php?mod=viewthread&tid=28730&extra=page=1
例子合集
一、二选一选择器
2.源代码 mux2.v
`timescale 1ns/1ps
//1ns 是步进 1ps是精度
module mux2(
a,
b,
sel,
out
);
input a;
input b;
input c;
output out;
assign out = sel?a:b; //sel为1时,out = a ,否则为 b
endmodule
2.激励信号 mux2_tb.v
`timescale 1ns /1ns
module mux2_td();
reg s_a;
reg s_b;
reg sel;
wire out;
mux2 mux2_tb( //模块名 例化标签名(缺少标签名仿真会报错)
.a(s_a), //连线自定义
.b(s_b),
.sel(sel),
.out(out)
);
initial begin
s_a=0;s_b=0;sel=0;
#200;
s_a=0;s_b=0;sel=1;
#200;
s_a=0;s_b=1;sel=0;
#200;
s_a=0;s_b=1;sel=1;
#200;
s_a=1;s_b=0;sel=0;
#200;
s_a=1;s_b=0;sel=1;
#200;
s_a=1;s_b=1;sel=0;
#200;
s_a=1;s_b=1;sel=1;
#200;
$stop;//仿真终止
end
endmodule
3.仿真图
二、三八译码器
1.源代码 decoder_3_8.v
`timescale 1ns / 1ns
module decoder_3_8(
a,
b,
c,
out
);
input a;
input b;
input c;
output reg [7:0]out; //always块中赋值的信号必须定义为reg型
always@(*)
case({a,b,c}) //{}位拼接符
3'b000:out=8'b0000_0001;
3'b001:out=8'b0000_0010;
3'b010:out=8'b0000_0100;
3'b011:out=8'b0000_1000;
3'b100:out=8'b0001_0000;
3'b101:out=8'b0010_0000;
3'b110:out=8'b0100_0000;
3'b111:out=8'b1000_0000;
default : out=8'b0000_0000; //默认项 以免情况没有罗列全出现锁存器
endcase
endmodule
2.激励信号 decoder_3_8_tb.v
`timescale 1ns / 1ns
module decoder_3_8_tb();
reg s_a;
reg s_b;
reg s_c;
wire [7:0]out;
decoder_3_8 decoder_3_8(
.a(s_a),
.b(s_b),
.c(s_c),
.out(out)
);
initial begin
s_a = 0; s_b = 0; s_c = 0;
#200;
s_a = 0; s_b = 0; s_c = 1;
#200;
s_a = 0; s_b = 1; s_c = 0;
#200;
s_a = 0; s_b = 1; s_c = 1;
#200;
s_a = 1; s_b = 0; s_c = 0;
#200;
s_a = 1; s_b = 0; s_c = 1;
#200;
s_a = 1; s_b = 1; s_c = 0;
#200;
s_a = 1; s_b = 1; s_c = 1;
#200;
$stop;
end
endmodule
3.仿真图
三、LED亮灭
1.源代码 LED_flash.v
`timescale 1ns / 1ns
module LED_flash(
CLK,
Reset,
LED
);
input CLK;
input Reset;
output reg LED;
reg [24:0] counter; //未定义位数则变为1位且一直为初值0
always@(posedge CLK or negedge Reset) //时钟上升沿与复位下降沿
if (!Reset)
counter <= 0; //非阻塞赋值
else if (counter == 24999999) //计数器计数时要减1 ,默认数字是十进制
//50HZ晶振两个上升沿间是20ns,5s就是500ms/20ns = 25_000_000次
counter <= 0;
else
counter <= counter + 1'b1;
//b 二进制
//d 十进制
//h 十六进制
always@(posedge CLK or negedge Reset) //一般将计数与LED状态分开写,方便调试
if (!Reset)
LED <= 0;
else if (counter == 24999999)
LED <= !LED;
endmodule
2.激励信号 LED_flash_tb.v
`timescale 1ns / 1ns
module LED_flash_tb();
reg CLK;
reg Reset;
wire LED;
LED_flash LED_flash(
.CLK(CLK),
.Reset(Reset),
.LED(LED)
);
//50HZ时钟激励信号
initial CLK = 1;
always #10 CLK = ~CLK;
initial begin
Reset = 0; //仿真复位结果
#201; //201避开时钟上升沿,方便观察
Reset = 1; //仿真正常结果,给出几个周期的时间
#2000000000;
$stop;
end
endmodule
3.仿真图
三、流水灯
1.源代码 LED_run.v
`timescale 1ns / 1ns
module LED_run(
CLK,
Reset,
LED
);
input CLK;
input Reset;
//方法一
// output reg [7:0] LED;
// reg [24:0] counter;
// always@(posedge CLK or negedge Reset)
// if (!Reset)
// counter <= 0;
// else if(counter == 24999)
// counter <= 0;
// else
// counter <= counter + 1'b1;
// always@(posedge CLK or negedge Reset)
// if (!Reset)
// LED <= 8'b0000_0001;
// else if(counter == 24999)
// begin
// if(LED == 8'b1000_0000)
// LED <= 8'b0000_0001;
// else
// LED <= LED << 1; //右移位运算(用0填补空位)
// end
// else
// LED <= LED;
//
//方法二/使用三八译码器例化模块/
output [7:0] LED;
//注意:调用的子模块,底层output已经定义过reg型,此处LED不能再次定义!!仿真会出错
//将decoder_3_8.V的程序复制到工程中
reg [24:0] counter; //时间计数器
reg [2:0] counter2; //流水灯选择器
always@(posedge CLK or negedge Reset)
if (!Reset)
counter <= 0;
else if(counter == 24999)
counter <= 0;
else
counter <= counter + 1'b1;
always@(posedge CLK or negedge Reset)
if (!Reset)
counter2 <= 0;
else if(counter == 24999)
counter2 <= counter2 +1'b1;
decoder_3_8 decoder_3_8(
.a(counter2[2]), //{a,b,c}对应{counter2[2],counter2[1],counter2[0]}
.b(counter2[1]),
.c(counter2[0]),
.out(LED) //三八译码器中已定义过out连线类型为reg
);
endmodule
2.激励信号 LED_run_tb.v
`timescale 1ns / 1ns
module LED_run_tb();
reg CLK;
reg Reset;
wire [7:0] LED;
LED_run LED_run(
.CLK(CLK),
.Reset(Reset),
.LED(LED)
);
initial CLK = 1;
always #10 CLK = !CLK;
initial begin
Reset = 0;
#201;
Reset = 1;
#40000;
end
endmodule
3.仿真图
四、让4个LED以不同频率亮灭(将变量参数化)
1.源代码
(1)子模块 LED_flash.v
`timescale 1ns / 1ps
module LED_flash(
CLK,
Reset,
LED
);
input CLK;
input Reset;
output reg LED;
reg [24:0] counter;
parameter MCNT = 2499; //参数化
always@(posedge CLK or negedge Reset)
if (!Reset)
counter <= 0;
else if (counter == MCNT)
counter <= 0;
else
counter <= counter + 1'b1;
always@(posedge CLK or negedge Reset)
if (!Reset)
LED <= 0;
else if (counter == MCNT)
LED <= !LED;
endmodule
(2)主程序 LED_run4.v
//用LED_Flash.v模块例化
`timescale 1ns / 1ns
module LED_run4(
CLK,
Reset,
LED
);
input CLK;
input Reset;
output [3:0] LED;
//1s 是25_000_000 - 1 = 24_999_999
//0.1s 是 25_000_00 - 1 = 24_999_99
//0.2s 是 50_000_00 - 1 = 49_999_99
//给4个LED灯做4次例化,并修改时间参数
LED_flash LED_flash_0( //模块名 例化标签名
.CLK(CLK),
.Reset(Reset),
.LED(LED[0])
);
defparam LED_flash_0.MCNT = 24999; //修改例化模块中的参数,注意是用引用标签名(方法一)
LED_flash LED_flash_1(
.CLK(CLK),
.Reset(Reset),
.LED(LED[1])
);
defparam LED_flash_1.MCNT = 49999;
LED_flash
#( .MCNT(74999) //修改例化模块中的参数(方法二)
)
LED_flash_2(
.CLK(CLK),
.Reset(Reset),
.LED(LED[2])
);
LED_flash
#( .MCNT(99999)
)
LED_flash_3(
.CLK(CLK),
.Reset(Reset),
.LED(LED[3])
);
endmodule
2.激励信号 LED_run4_tb.v
`timescale 1ns / 1ns
module LED_run4_tb();
reg CLK;
reg Reset;
wire [3:0]LED;
LED_run4 LED_run4( //激励信号例化 主程序的模块名
.CLK(CLK),
.Reset(Reset),
.LED(LED)
);
initial CLK = 1;
always #10 CLK = ~CLK;
initial begin
Reset = 0;
#201;
Reset = 1;
#2000000000;
end
endmodule
五、从计数器到可控线性序列机———LED 实验进化六部曲
(为后续串口通信做基础准备)
1.让LED灯按照亮0.25秒,灭0.75秒的状态循环亮灭
2. 让LED灯按照亮0.25秒,灭0.5秒、亮0.75秒,灭1秒的状态循环亮灭
3. 让LED 灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。以0.25秒为一个变化周期,8个变化状态为一个循环
4. 让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。8个变化状态为 一个循环,每个变化状态的时间值可以根据不同的应用场景选择
5.让多个LED 灯按照设置的模式各自在一个变化循环内独立亮灭变化
6.每隔10ms,让 LED灯的一个8状态循环执行一次(每个状态的变化时间值小一点,方便测试,比如设置为10us)
1:让LED灯按照灭0.25秒,亮0.75秒的状态循环亮灭。
1.源代码 counter_LED_1.v
`timescale 1ns / 1ns
module counter_LED(
CLK,
Reset,
LED
);
input CLK;
input Reset;
output reg LED;
reg [26:0] counter;
parameter MCNT = 12500000; //0.25s的计数器 1s/4/20ns=12500000
always@(posedge CLK or negedge Reset)
if (!Reset)
counter <= 0;
else if (counter == MCNT*4 - 1)//定义一个1s的总时钟
counter <= 0;
else
counter <= counter + 1'b1;
always@(posedge CLK or negedge Reset) //一般将计数与LED状态分开写,方便调试
if (!Reset)
LED <= 0;
else if (counter == MCNT - 1)//0.25s时LED为1
LED <= 1;
else if (counter == MCNT*4 - 1)//0.25s+0.75s时LED为0
LED <= 0;
endmodule
2.激励信号 counter_LED_1_tb.v
`timescale 1ns / 1ns
module counter_LED_tb();
reg CLK;
reg Reset;
wire LED;
counter_LED
#(
.MCNT (12500) //缩短仿真时间
)
counter_LED(
.CLK(CLK),
.Reset(Reset),
.LED(LED)
);
initial CLK = 1;
always #10 CLK = ~CLK;
initial begin
Reset = 0;
#201;
Reset = 1;
#2000000000;
end
endmodule
3.仿真图
2:让LED灯按照亮0.25秒,灭0.5秒,亮0.75秒,灭1秒的状态循环亮灭。
1.源代码 counter_LED_2.v
`timescale 1ns / 1ns
module counter_LED_2(
CLK,
Reset,
LED
);
input CLK;
input Reset;
output reg LED;
reg [23:0] counter;
parameter MCNT = 12500000; //0.25s
always@(posedge CLK or negedge Reset)
if(!Reset)
counter <= 0;
else if(counter == MCNT*10 - 1)//1s
counter <= 0;
else
counter <= counter +1'b1;
always@(posedge CLK or negedge Reset)
if(!Reset)
LED <= 0;
else if(counter == MCNT - 1)//0.25s
LED <= 0;
else if(counter == MCNT*3 -1)//0.25+0.5
LED <= 1;
else if(counter == MCNT*6 -1)//0.25+0.5+0.75
LED <= 0;
else if(counter == MCNT*10 -1)//0.25+0.5+0.75+1
LED <= 1;
endmodule
2.激励信号 counter_LED_2_tb.v
`timescale 1ns / 1ps
module counter_LED_2_tb( );
reg CLK;
reg Reset;
wire LED;
counter_LED_2
#(
.MCNT (12500) //缩短仿真时间
)
counter_LED_2(
.CLK(CLK),
.Reset(Reset),
.LED(LED)
);
initial CLK = 1;
always #10 CLK = ~CLK;
initial begin
Reset = 0;
#201;
Reset = 1;
#2000000000;
end
endmodule
3.仿真图
3:让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。以0.25秒为一个变化周期, 8个变化状态为一个循环。
分析:2秒为一个循环周期;有指定亮灭的端口输入。
1.源代码 counter_LED_3.v
`timescale 1ns / 1ns
module counter_LED_3(
CLK,
Reset,
Ctrl, //指定亮灭的输入信号
LED
);
input CLK;
input Reset;
input [7:0] Ctrl;
output reg LED;
reg [26:0] counter;
parameter MCNT = 12500000; //0.25s
always@(posedge CLK or negedge Reset)
if (!Reset)
counter <= 0;
else if (counter == MCNT*8 - 1)//定义一个2s的总时钟
counter <= 0;
else
counter <= counter + 1'b1;
//else if 语句
// always@(posedge CLK or negedge Reset)
// if (!Reset)
// LED <= 0;
// else if (counter == MCNT - 1)
// LED <= Ctrl[0];
// else if (counter == MCNT*2 - 1)
// LED <= Ctrl[1];
// else if (counter == MCNT*3 - 1)
// LED <= Ctrl[2];
// else if (counter == MCNT*4 - 1)
// LED <= Ctrl[3];
// else if (counter == MCNT*5 - 1)
// LED <= Ctrl[4];
// else if (counter == MCNT*6 - 1)
// LED <= Ctrl[5];
// else if (counter == MCNT*7 - 1)
// LED <= Ctrl[6];
// else if (counter == MCNT*8 - 1)
// LED <= Ctrl[7];
//case语句
always@(posedge CLK or negedge Reset)
if (!Reset)
LED <= 0;
else case(counter)
MCNT * 1 - 1: LED <= Ctrl[0];
MCNT * 2 - 1: LED <= Ctrl[1];
MCNT * 3 - 1: LED <= Ctrl[2];
MCNT * 4 - 1: LED <= Ctrl[3];
MCNT * 5 - 1: LED <= Ctrl[4];
MCNT * 6 - 1: LED <= Ctrl[5];
MCNT * 7 - 1: LED <= Ctrl[6];
MCNT * 8 - 1: LED <= Ctrl[7];
default : LED <= LED;
endcase
endmodule
2.激励信号 counter_LED_3_tb.v
`timescale 1ns / 1ns
module counter_LED_td();
reg CLK;
reg Reset;
reg [7:0]Ctrl;
wire LED;
counter_LED_3
#(
.MCNT (12500) //减少仿真时间
)
counter_LED_3(
.CLK(CLK),
.Reset(Reset),
.Ctrl(Ctrl),
.LED(LED)
);
initial CLK = 1;
always #10 CLK = ~CLK;
initial begin
Reset = 0;
Ctrl = 0;
#201;
Reset = 1;
#2000
Ctrl = 8'b1000_0110; //自定义 LED将根据Ctrl的变化而变化
#20000000;
Ctrl = 8'b1100_1110;
#20000000;
$stop;
end
endmodule
3.仿真图
4:让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。8个变化状态为一个循环,每个变化状态的时间值可以根据不同的应用场景选择。
分析:循环周期时长自定义;有指定亮灭的端口输入。
1.源代码 counter_LED_4.v
`timescale 1ns / 1ps
module counter_LED_4(
CLK,
Reset,
Ctrl,
Time,
LED
);
input CLK;
input Reset;
input [7:0] Ctrl;
input [31:0] Time;
output reg LED;
reg [31:0] counter;
reg [2:0] counter2;
always@(posedge CLK or negedge Reset)
if (!Reset)
counter <= 0;
else if (counter == Time - 1)//定义一个Time变化周期 ,一拍是几秒
counter <= 0;
else
counter <= counter + 1'b1;
always@(posedge CLK or negedge Reset)
if (!Reset)
counter2 <= 0;
else if (counter == Time - 1) //满一拍计数加1,三位满7(111)自动循环
counter2 <= counter2 + 1'b1;
always@(posedge CLK or negedge Reset)
if (!Reset)
LED <= 0;
else case(counter2)
0: LED <= Ctrl[0];
1: LED <= Ctrl[1];
2: LED <= Ctrl[2];
3: LED <= Ctrl[3];
4: LED <= Ctrl[4];
5: LED <= Ctrl[5];
6: LED <= Ctrl[6];
7: LED <= Ctrl[7];
default : LED <= LED;
endcase
endmodule
2.激励信号 counter_LED_4_tb.v
`timescale 1ns / 1ps
module counter_LED_4_tb( );
reg CLK;
reg Reset;
reg [7:0] Ctrl;
reg [31:0] Time;
wire LED;
counter_LED_4 counter_LED_4(
.CLK(CLK),
.Reset(Reset),
.Ctrl(Ctrl),
.Time(Time),
.LED(LED)
);
initial CLK = 1;
always #10 CLK = ~CLK;
initial begin
Reset = 0;
Ctrl = 0;
Time = 0;
#201
Reset = 1;
#2000
Time = 12500; //自定义
Ctrl = 8'b1000_0110; //自定义
#20000000;
Ctrl = 8'b1100_1110;
#20000000;
$stop;
end
endmodule
3.仿真图
5:让多个LED 灯按照设置的模式各自在一个变化循环内独立亮灭变化。
分析:时间不变,同时控制多个输出信号.
1.源代码 counter_LED_5.v
`timescale 1ns / 1ns
module counter_LED_5(
CLK,
Reset,
CtrlA,
CtrlB,
Time, //定义一个时间单位
LED
);
input CLK;
input Reset;
input [7:0] CtrlA,CtrlB;
input [31:0] Time;
output reg [1:0] LED;
reg [31:0] counter;
reg [2:0] counter2; //第二个计数器,8个时间单位一个周期
always@(posedge CLK or negedge Reset)
if (!Reset)
counter <= 0;
else if (counter == Time - 1)
counter <= 0;
else
counter <= counter + 1'b1;
always@(posedge CLK or negedge Reset)
if (!Reset)
counter2 <= 0;
else if (counter == Time - 1)
counter2 <= counter2 + 1'b1;
always@(posedge CLK or negedge Reset)
if (!Reset)
LED <= 0;
else case(counter2)
0: begin
LED[0] <= CtrlA[0];
LED[1] <= CtrlB[0];
end
1: begin
LED[0] <= CtrlA[1];
LED[1] <= CtrlB[1];
end
2: begin
LED[0] <= CtrlA[2];
LED[1] <= CtrlB[2];
end
3: begin
LED[0] <= CtrlA[3];
LED[1] <= CtrlB[3];
end
4: begin
LED[0] <= CtrlA[4];
LED[1] <= CtrlB[4];
end
5: begin
LED[0] <= CtrlA[5];
LED[1] <= CtrlB[5];
end
6: begin
LED[0] <= CtrlA[6];
LED[1] <= CtrlB[6];
end
7: begin
LED[0] <= CtrlA[7];
LED[1] <= CtrlB[7];
end
default : LED <= LED;
endcase
endmodule
2.激励信号 counter_LED_5_tb.v
`timescale 1ns / 1ns
module counter_LED_td();
reg CLK;
reg Reset;
reg [7:0]CtrlA;
reg [7:0]CtrlB;
reg [31:0]Time;
wire [1:0] LED;
counter_LED_5 counter_LED_5(
.CLK(CLK),
.Reset(Reset),
.CtrlA(CtrlA),
.CtrlB(CtrlB),
.Time(Time),
.LED(LED)
);
initial CLK = 1;
always #10 CLK = ~CLK;
initial begin
Reset = 0;
CtrlA = 0;
CtrlB = 0;
Time = 0;
#201;
Reset = 1;
#2000
Time = 12500;
CtrlA = 8'b1000_0110; //自定义
CtrlB = 8'b1001_0110; //自定义
#20000000;
CtrlA = 8'b1100_1110;
CtrlB = 8'b1010_0110; //自定义
#20000000;
$stop;
end
endmodule
3.仿真图
6:每隔10ms,让 LED灯的一个8状态循环执行一次(每个状态的变化时间值小一点,方便测试, 比如设置为10us)。
分析: 以10ms为一个周期,8个状态为一组,8个状态结束后停止,直到下一个周期开始时进入下一个8个状态循环。
1.源代码 counter_LED_6.v
`timescale 1ns / 1ns
module counter_LED_6(
CLK,
Reset,
Ctrl,
Time,
LED
);
input CLK;
input Reset;
input [31:0] Time;
input [7:0] Ctrl;
output reg LED;
reg [18:0] counter0; //10ms 的计数器,500 000
reg [31:0] counter1; //EN为1 时计数器
reg [2:0] counter2; //8个Time的计数器
reg EN;
//10ms的周期计数器
always@(posedge CLK or negedge Reset)
if (!Reset)
counter0 <= 0;
else if (counter0 == 500_000 - 1) //10ms
counter0 <= 0;
else
counter0 <= counter0 + 1'b1;
//EN状态 ,10msEN变高,8拍结束EN变低
always@(posedge CLK or negedge Reset)
if (!Reset)
EN <= 0;
else if (counter0 == 0)
EN <= 1;
else if ((counter2 == 7) && (counter1 == Time - 1))
//状态持续时间结束后才复位,若没有(counter == Time - 1),两个周期后EN就置0,导致最后一位数据无法保持
EN <= 0;
//EN为1时的计数器
always@(posedge CLK or negedge Reset)
if (!Reset)
counter1 <= 0;
else if (EN) begin
if (counter1 == Time - 1)
counter1 <= 0;
else
counter1 <= counter1 + 1'b1;//在EN有效期内,循环计数
end
else
counter1 <= 0;
//EN为1时的拍子计数器,数8拍
always@(posedge CLK or negedge Reset)
if (!Reset)
counter2 <= 0;
else if (EN) begin
if (counter1 == Time - 1)
counter2 <= counter2 + 1'b1;//在EN有效期内,循环数拍
end
else
counter2 <= 0;
//控制LED
always@(posedge CLK or negedge Reset)
if (!Reset)
LED <= 0;
else case(counter2)
0: LED <= Ctrl[0];
1: LED <= Ctrl[1];
2: LED <= Ctrl[2];
3: LED <= Ctrl[3];
4: LED <= Ctrl[4];
5: LED <= Ctrl[5];
6: LED <= Ctrl[6];
7: LED <= Ctrl[7];
default : LED <= LED;
endcase
endmodule
2.激励信号 counter_LED_6_tb.v
`timescale 1ns / 1ps
module counter_LED_6_tb( );
reg CLK;
reg Reset;
reg [7:0] Ctrl;
reg [31:0] Time;
wire LED;
counter_LED_6 counter_LED_6(
.CLK(CLK),
.Reset(Reset),
.Ctrl(Ctrl),
.Time(Time),
.LED(LED)
);
initial CLK = 1;
always #10 CLK = ~CLK;
initial begin
Reset = 0;
Ctrl = 0;
Time = 0;
#201
Reset = 1;
#2000
Time = 50_000; //自定义
Ctrl = 8'b1000_0110; //自定义
#20000000;
Ctrl = 8'b1100_1110;
#20000000;
$stop;
end
endmodule
3.仿真图
六、阻塞赋值与非阻塞赋值
上文中出现过的赋值语句,如:
always(*)
case({s,b,c})
3'b000 : out = 8'b0000_0001; //不是阻塞赋值,也不是非阻塞赋值,因为不是时序逻辑
always(posedge CLK)
counter <= counter + 1'b; //非阻塞赋值
注意:阻塞赋值与非阻塞赋值仅在时序逻辑中才存在
七、串口(UART)发送实验
1、串口通信模块设计的目的是用来发送数据的,因此需要一个数据输入端口(Ctrl)
2、串口通信,支持不同的波特率,所以需要有一个比特率设置端口(Time)
3、串口通信的本质就是将8位的并行数据通过一根信号线,在不同的时刻传输并行数据的不同位,通过多个时刻,最终将8位并行数据全部传出(LED)
4、串口通信以1位的低电平标志串行传输的开始,待8位数据传输完成之后,再以1位的高电平标志传输的结束
5、控制信号,控制并转串模块什么时刻开始工作,什么时候数据发送完成?需要有一个发送开始信号,以及一个发送完成信号(EN)
附:波特率计算
附:计数发送节拍
1:发送数据(不成熟版本)
1.源代码 .v
`timescale 1ns / 1ns
module uart_byte_tx(
Clk,
Reset_n,
Data,
Send_en,
Baud_set,
uart_tx,
Tx_done
);
input Clk;
input Reset_n;
input [7:0]Data;
input Send_en;
input [2:0]Baud_set;
output reg uart_tx;
output reg Tx_done;
//波特率计数器计算================================
// Baud_set = 0 波特率 = 9600;
// Baud_set = 1 波特率 = 19200;
// Baud_set = 2 波特率 = 38400;
// Baud_set = 3 波特率 = 57600;
// Baud_set = 4 波特率 = 112500;
//1s = 1_000_000_000ns 50MHz两个上升沿之间是1_000_000_000ns/50_000_000 = 20ns
//1_000_000_000/9600/20 = bps_DR = 5208 (数拍子要-1)
reg [17:0]bps_DR;
always@(*)
case(Baud_set)
0: bps_DR = 1000000000/9600/20;
1: bps_DR = 1000000000/19200/20;
2: bps_DR = 1000000000/38400/20;
3: bps_DR = 1000000000/57600/20;
4: bps_DR = 1000000000/115200/20;
default:bps_DR = 1000000000/9600/20;
endcase
reg [17:0]div_cnt; //分频得到基本时钟,每个数据位需要保持的时间(bps_DR的计数器)
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <= 0;
else if(Send_en)
begin
if(div_cnt == bps_DR - 1)
div_cnt <= 0;
else
div_cnt <= div_cnt +1'b1;
end
else
div_cnt <= 0;
reg [3:0]bps_cnt; //满一拍,为一位,发送一次数据一共要11拍(11拍的计数器)
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bps_cnt <= 0;
else if(Send_en)
begin
if(div_cnt == bps_DR - 1)
begin
if(bps_cnt == 11)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt +1'b1;
end
end
else
bps_cnt <= 0;
//发送Tx数据和Tx_done信号
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
begin
uart_tx <= 1'b1;
Tx_done <= 1'b0;
end
else
begin
case(bps_cnt)
0: begin uart_tx <= 0;Tx_done <= 1'b0; end
1: uart_tx <= Data[0];
2: uart_tx <= Data[1];
3: uart_tx <= Data[2];
4: uart_tx <= Data[3];
5: uart_tx <= Data[4];
6: uart_tx <= Data[5];
7: uart_tx <= Data[6];
8: uart_tx <= Data[7];
9: uart_tx <= 1;
10:begin uart_tx <= 1; Tx_done <= 1'b1; end
default:uart_tx <= 1;
endcase
end
endmodule
2.激励信号 tb.v
`timescale 1ns / 1ns
module uart_byte_tx_tb;
reg Clk;
reg Reset_n;
reg [7:0] Data;
reg Send_en;
reg [2:0] Baud_set;
wire uart_tx;
wire Tx_done;
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Reset_n(Reset_n),
.Data(Data),
.Send_en(Send_en),
.Baud_set(Baud_set),
.uart_tx(uart_tx),
.Tx_done(Tx_done)
);
//50MHz激励信号
initial Clk = 1;
always #10 Clk = ~Clk;
//
initial
begin
//复位状态
Reset_n = 0;
Data = 0;
Send_en = 0;
Baud_set = 4;//波特率设置为115200
#201;
//运行状态
Reset_n = 1;
#100;
Data = 8'h57;
Send_en = 1'b1;
#20;
@(posedge Tx_done);//死循环等待Tx_done为1后进入下一步
Send_en = 0;
#20000;
Data = 8'h75;
Send_en = 1;
#20;
@(posedge Tx_done);//死循环等待Tx_done为1后进入下一步
Send_en = 0;
#20000;
$stop;
end
endmodule
3.仿真图
八、
1.源代码 .v
`timescale 1ns / 1ns
2.激励信号 tb.v
`timescale 1ns / 1ns
3.仿真图
总结
以上就是本文要讲的内容。
1.源代码
在这里插入代码片
2.激励文件
在这里插入代码片