赞
踩
目标:写一套硬件描述语言,能够在指定平台实现相应功能
1.设计定义(led一秒闪烁一次)
2.设计输入(编写逻辑verilog描述,画逻辑图,使用ip核(例如调用厂家写好的fft))
3.综合工具(由专业的EDA提供,vivado,quartus),对所写的逻辑进行分析综合,得到逻辑门级别的电路内容RTL
4.功能仿真(使用专门的仿真工具modelsim进行仿真,验证设计的逻辑功能能够实现,仿真基本接近真实情况,可信度比较高)
5.布局布线,在指定的器件上将设计的逻辑电路实现,布线不合理主频低。
6.分析性能(vivado)
1).时序仿真,布线后可进行时序仿真,看输入输出的延迟能不能达到要求,非常耗时
2).静态时序分析,常用。
性能不满足回到设计输入修改,
7.下载到目标板上运行,查看运行结果。ILA,signaltap调试
让设计的逻辑在目标板上正常工作。
1.源文件如下
- `timescale 1ns / 1ps
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/04/20 15:42:23
- // Design Name:
- // Module Name: xmgmux2
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
-
- module xmgmux2(
- a,
- b,
- sel,
- out
- );
- input a;
- input b;
- input sel;
- output out;
-
- assign out = (sel==1)?a:b;
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
2.写好源文件后runsynthesis看看有没有语法错误
3.编写测试文件,测试文件的功能是给设计好的模块,施加激励,使之有输出,然后查看输入输出是否符合设计要求。
tb文件如下
- `timescale 1ns / 1ps//1ns时间的步进
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/04/20 15:53:53
- // Design Name:
- // Module Name: mux2_tb
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
-
- module mux2_tb(
- //测试文件不需要端口
- );
- reg s_a;//激励定义成reg型
- reg s_b;
- reg sel;
- wire out;//输出接口定义成wire型
-
- xmgmux2 mux2inst0(
- .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;
-
-
- end
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
总结来说就是 设计几个激励源接入到模块中
之后调用initial begin-end 设置激励源的运行规则 比如说三个激励源依次高电平,一共有八种状态
其中#200 为延时函数,只能在tb文件中使用,由于`timescale 1ns / 1ps,因此#200为延时200ns
4.编写好tb文件后保存,可以再runsynthesis看看有没有语法错误,之后运行仿真
仿真中有两个比较典型的 一个是Run Behavioral Simulation另一个是Run Post-implementation Timing Simulation,前一个是行为仿真,不存在时序前后的问题,后一个可以查看时序的先后(如输出信号和激励信号之间有延时),有时还会出现竞争关系产生的毛刺。
5.在进行仿真之后。可以图形化映射引脚,先点击SYNTHESIS下的Open synthesized Design
之后打开Layout的I/O Planing
引脚映射完成后一定要CTRL+S保存
6.生成比特流文件。下载
计数器原理
源代码
- module cnt_led_flash(
- led,
- rst_n,
- clk
-
- );
- input clk;
- input rst_n;
- output reg led;
-
- reg [24:0] cnt;
- //<= 非阻塞赋值 暂时理解为“=”
- /* always@(posedge clk or negedge rst_n)
- if(!rst_n)begin
- cnt <= 0;
- led <=0;
- end
- else if(cnt == 25000000)begin
- led <= !led;//若要在always块中赋值led 则led必须是reg型
- cnt <= 0;
- end
- else
- cnt <= cnt + 1'd1;
- */
- /* led和cnt分开写有助于编译器生成更简洁的逻辑门电路 */
- always@(posedge clk or negedge rst_n)
- if(!rst_n)
- cnt <= 0;
- else if(cnt == 24999999)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
- cnt <= 0;
- else
- cnt <= cnt + 1'd1;
-
- always@(posedge clk or negedge rst_n)
- if(!rst_n)
- led <=0;
- else if(cnt == 24999999)
- led <= !led;//若要在always块中赋值led 则led必须是reg型
-
-
-
-
-
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
之后编写testbench
- `timescale 1ns / 1ns
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/04/22 20:23:37
- // Design Name:
- // Module Name: cnt_led_flash_tb
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
-
- module cnt_led_flash_tb(
- //无需输入输出端
- );
- //定义激励信号
- reg clk;
- reg rst;
- wire led;
- /* 将激励信号接入模块 */
- cnt_led_flash cnt_led_flashinst0(
- .led(led),
- .rst_n(rst),
- .clk(clk)
- );
- /* 产生激励信号的逻辑 */
- initial clk = 1;
- always #10 clk = !clk;//clk生成了周期10ns的时钟信号
- initial begin
- rst = 0;
- #201;//时钟沿之后一点点
- rst = 1;
- #2000000000;
- $stop;
- end
-
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
保存后 run simulation,可能会持续很久,因为需要24999999个上升沿
上升沿间隔500ms
之后上板验证
移位操作跑马灯 源文件
- `timescale 1ns / 1ps
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/04/23 11:26:25
- // Design Name:
- // Module Name: led_run
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
-
- module led_run(
- rst_n,
- clk,
- led
- );
-
- input rst_n;
- input clk;
- output reg [7:0] led;//八位跑马灯 在always赋值,reg型
- reg [24:0] cnt;//500毫秒延时 25位
-
- always@(posedge clk or negedge rst_n)
- if(!rst_n)//复位就是复位,不要和后面的操作合并如if((!rst_n)||(cnt == 25000000-1)),哪怕逻辑一样也不行
- cnt <= 0;
- else if(cnt == 25000-1)//时间缩短,方便调试
- /* else if(cnt == 25000000-1) */
- cnt <= 0;
- else
- cnt <= cnt +1'b1;//赋值要加位宽限定,判断的时候可以不加
- //移位法实现跑马灯 0000 0001→0000 0010......
- always@(posedge clk or negedge rst_n)
- if(!rst_n)
- led <= 8'b0000_0001;
- else if(cnt == 25000-1)begin//只有一句不用begin——end,多句需要begin——end
- if(led == 8'b1000_0000)
- led <= 8'b0000_0001;
- else
- led <= led << 1;
- end
- else
- led <= led;//写不写都行,时序逻辑默认保持状态
-
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
tb文件
- `timescale 1ns / 1ns
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/04/23 11:49:21
- // Design Name:
- // Module Name: led_run_tb
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
-
- module led_run_tb(
- //仿真不需要输入输出端口
- );
- reg rst_n;
- reg clk;
- wire [7:0] led;
-
- /* 将激励输出接口接入模块 */
- led_run led_runinst0(
- .rst_n(rst_n),
- .clk(clk),
- .led(led)
- );
- //编写激励逻辑
- initial clk = 1;
- always #10 clk =!clk;//clk产生时钟周期
- initial begin
- rst_n = 0;
- #201;
- rst_n = 1;
- #4000000;//延时五秒
- $stop;
-
-
- end
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
位拼接操作跑马灯 源文件
原理如下
跑马灯中调用38译码器
首先我们先编写一个源文件led_run_2,这个源文件中要调用其他源文件decoder_3_8模块
之后我们找到待调用的模块的源文件的地址 (也可以自己creat file)
D:\ZYNQ_Code\decoder_3_8\decoder_3_8.srcs\sources_1\new
复制.v文件到本工程源文件路径
之后再vivado中添加这个源文件
之后在.v文件中调用模块,语法与编写tb文件类似
需要注意的是,由于decoder_3_8模块(底层)中已经定义了out位reg型
因此led_run_2模块中无需再将led赋值成为reg型
1.仿真时修改某一参数使之适合仿真
由于流水灯是500ms变化一次,对于仿真来说过于耗时,因此仿真时会将计数器修改成24999(500ms对应24999999),但是在源文件里修改非常的麻烦,板级调试时还要改回来。因此将cnt参数化,在.V文件里是板级值,在tb文件里重定义成仿真需要的值。
原代码如下
将最大计数值参数化
在tb文件中调用此模块并且修改参数,只需要在模块名和标签名之前加入#()并且在其中修改参数即可
2.在某.V文件中调用其他.v文件的模块并且修改其中的参数
假如说在38译码器模块中有一个参数width(输出位宽)
如果想在流水灯中调用38译码器并且修改此模块参数
defparam 模块标签名.参数名 = 目标值; 如 defparam decoder_3_8inst0.width = 8;
运行后没有报错,但是如果把width改成5,就会使输出有三位是高阻态。
另外,在.v文件中修改参数也可以用tb中的#()语法,但是defparam不可以用在tb文件中。
亮0.5灭0.5设计思想:之前是计数器计数满(0.5s)led反转; 另一种设计思路,计数器记满1s(49999999)归零,其中cnt=24999999(MCNT/2-1)高电平,cnt=49999999低电平
- //亮0.5灭0.5
- module led_evo_1(
- clk,
- rst_n,
- led
- );
-
- input clk;
- input rst_n;
- output reg led;
- reg [25:0] cnt;//计数1s
- parameter MCNT = 50000000;
- //<= 非阻塞赋值 暂时理解为“=”
- /* led和cnt分开写有助于编译器生成更简洁的逻辑门电路 */
- always@(posedge clk or negedge rst_n )
- if(!rst_n)
- cnt <= 0;
- else if(cnt == MCNT-1)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
- cnt <= 0;
- else
- cnt <= cnt + 1'd1;
- always@(posedge clk or negedge rst_n)
- if(!rst_n)
- led <=0;
- else if(cnt == MCNT/2 - 1)
- led <= 1;//若要在always块中赋值led 则led必须是reg型
- else if(cnt == MCNT-1)
- led <= 0;
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
由此可以完成亮0.25灭0.75,只需要将亮的时间改一下
结果如下
思路,计数周期2.5s 其中0-0.25亮 0.25-0.75s灭 0.75-1.5亮 1.5-2.5灭
tb
- module led_evo_1_tb(
- //仿真不需要输入输出端口
- );
- reg rst_n;
- reg clk;
- wire led;
-
- /* 将激励输出接口接入模块 */
- led_evo_2
- #(.MCNT(125000))
- led_evo_2inst0(
- .clk(clk),
- .rst_n(rst_n),
- .led(led)
- );
-
- //编写激励逻辑
- initial clk = 1;
- always #10 clk =!clk;//clk产生时钟周期
- initial begin
- rst_n = 0;
- #201;
- rst_n = 1;
- #2000000000;//延时2秒
- $stop;
-
-
- end
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
设计思路;定义一个8位的变量[7:0],每一位储存一个状态,然后将计数周期(2s)分为八份(0.25)每份对应变量的每一位
.V
tb
结果
其中scope可以查看文件中变量的值
思路,两个计数器,一个计数器计数基本时间(一个状态),第二个计数器数八个基本事件单位
.v
- module led_evo_4(
- clk,
- rst_n,
- ctrl,
- Time,
- led
- );
-
- input clk;
- input rst_n;
- input [7:0] ctrl;
- input [31:0] Time;
- output reg led;
- reg [31:0] cnt;//计数2s
- /* parameter MCNT = 100000000;//2s/20ns=100000000 */
- //<= 非阻塞赋值 暂时理解为“=”
- /* led和cnt分开写有助于编译器生成更简洁的逻辑门电路 */
- always@(posedge clk or negedge rst_n )
- if(!rst_n)
- cnt <= 0;
- else if(cnt == Time-1)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
- cnt <= 0;
- else
- cnt <= cnt + 1'd1;
- reg [2:0] cnt2;
- always@(posedge clk or negedge rst_n )
- if(!rst_n)
- cnt2 <= 0;
- else if(cnt == Time-1)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
- cnt2 <= cnt2 + 1'b1;
- always@(posedge clk or negedge rst_n)
- if(!rst_n)
- led <=0;
- else case (cnt2)
- 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
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
tb
- module led_evo_1_tb(
- //仿真不需要输入输出端口
- );
- reg rst_n;
- reg [7:0] ctrl;
- reg clk;
- reg [31:0] Time;
- wire led;
-
- /* 将激励输出接口接入模块 */
- led_evo_4 led_evo_4inst0(
- .clk(clk),
- .rst_n(rst_n),
- .ctrl(ctrl),
- .Time(Time),
- .led(led)
- );
-
- //编写激励逻辑
- initial clk = 1;
- always #10 clk =!clk;//clk产生时钟周期
- initial begin
- rst_n = 0;
- ctrl = 0;
- Time = 0;
- #201;
- rst_n = 1;
- #2000;
- Time = 2500;
- ctrl = 8'b1000_0001;
- #2000000000;//延时2秒
- $stop;
-
-
- end
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
调节time以及ctrl即可
思路:多个ctrl[7:0]控制不同led的一字节亮灭
.v
- module led_evo_5(
- clk,
- rst_n,
- ctrlA,
- ctrlB,
- Time,
- led
- );
- input clk;
- input rst_n;
- input [7:0] ctrlA;
- input [7:0] ctrlB;
- input [31:0] Time;
- output reg [1:0] led;
- reg [31:0] cnt;
- always@(posedge clk or negedge rst_n )
- if(!rst_n)
- cnt <= 0;
- else if(cnt == Time-1)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
- cnt <= 0;
- else
- cnt <= cnt + 1'd1;
- reg [2:0] cnt2;
- always@(posedge clk or negedge rst_n )
- if(!rst_n)
- cnt2 <= 0;
- else if(cnt == Time-1)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
- cnt2 <= cnt2 + 1'b1;
- always@(posedge clk or negedge rst_n)
- if(!rst_n)
- led <=0;
- else case (cnt2)
- 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
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
tb
- module led_evo_1_tb(
- //仿真不需要输入输出端口
- );
- reg rst_n;
- reg [7:0] ctrlA;
- reg [7:0] ctrlB;
- reg clk;
- reg [31:0] Time;
- wire [1:0] led;
-
- /* 将激励输出接口接入模块 */
- led_evo_5 led_evo_5inst0(
- .clk(clk),
- .rst_n(rst_n),
- .ctrlA(ctrlA),
- .ctrlB(ctrlB),
- .Time(Time),
- .led(led)
- );
-
- //编写激励逻辑
- initial clk = 1;
- always #10 clk =!clk;//clk产生时钟周期20ns 50Mhz
- initial begin
- rst_n = 0;
- ctrlA = 0;
- ctrlB = 0;
- Time = 0;
- #201;
- rst_n = 1;
- #2000;
- Time = 2500;
- ctrlA = 8'b1000_0001;
- ctrlB =8'b0111_1110;
- #2000000000;//延时2秒
- $stop;
-
-
- end
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
结果
思路控制cnt0每10ms计数一次 添加EN参数,EN高电平时开始产生状态序列
.v文件
- module led_evo_6(
- clk,
- rst_n,
- ctrla,
- timea,
- led
- );
-
- input clk;
- input rst_n;
- input [7:0] ctrla;
- input [31:0] timea;
- output reg led;
- reg EN;
- reg [18:0] cnt0;//计数10ms]
- reg [2:0] cnt2;
- reg [31:0] cnt1;
-
- //10ms计数器
- always@(posedge clk or negedge rst_n)
- if(!rst_n)
- cnt0 <= 0;
- else if(cnt0 == 500000-1)//计数10ms
- cnt0 <= 0;
- else
- cnt0 <= cnt0 + 1'b1;
- always@(posedge clk or negedge rst_n)
- if(!rst_n)
- EN <= 0;
- else if(cnt0 == 0)
- EN <= 1;
- else if (cnt2 == 7 && cnt1 == timea -1)
- EN <= 0;
-
-
- //timea计数器
- always@(posedge clk or negedge rst_n )//led0一个状态持续时间
- if(!rst_n)
- cnt1 <= 0;
- else if(EN)begin
- if(cnt1 == timea-1)
- cnt1 <= 0;
- else
- cnt1 <= cnt1 + 1'd1;
- end
- else cnt1 <= 0;
-
- always@(posedge clk or negedge rst_n )//led0 8个状态
- if(!rst_n)
- cnt2 <= 0;
- else if(EN)begin
- if(cnt1 == timea-1)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
- cnt2 <= cnt2 + 1'b1;
- end
- else
- cnt2 <= 0;
- always@(posedge clk or negedge rst_n)
- if(!rst_n)
- led <=0;
- else case (cnt2)
- 0 : led <= ctrla[0];
- 1 : led <= ctrla[1];
- 2 : led <= ctrla[2];
- 3 : led <= ctrla[3];
- 4 : led <= ctrla[4];
- 5 : led <= ctrla[5];
- 6 : led <= ctrla[6];
- 7 : led <= ctrla[7];
- default: led<=led;//其他情况保持原值不变
- endcase
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
tb文件
- module led_evo_1_tb(
- //仿真不需要输入输出端口
- );
- reg rst_n;
- reg [7:0] ctrlA;
- reg clk;
- reg [31:0] TIMEA;
- wire led;
-
- /* 将激励输出接口接入模块 */
- led_evo_6 led_evo_6inst0(
- .clk(clk),
- .rst_n(rst_n),
- .ctrla(ctrlA),
- .timea(TIMEA),
- .led(led)
- );
-
- //编写激励逻辑
- initial clk = 1;
- always #10 clk =!clk;//clk产生时钟周期20ns 50Mhz
- initial begin
- rst_n = 0;
- ctrlA = 0;
- TIMEA = 0;
- #201;
- rst_n = 1;
- #2000;
- TIMEA = 2500;
- ctrlA = 8'b1000_0110;
- #2000000000;//延时2秒
- $stop;
-
-
- end
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
阻塞赋值与非阻塞赋值的概念只在时序逻辑中才存在。
always@(*)
case(a,b,c)
0:out = 8'b00000001; 这段既不属于阻塞也不属于非阻塞
阻塞赋值写出来的电路,代码先后顺序不一样,综合出来的电路也可能不一样。
逻辑电路不同就会导致时序图有区别.
1.数据输入端口
2.波特率设置端口
3.八位并行数据TX线串行输出
4.串口通信以一位低电平标志传输开始,8位数据传输完成之后,一位高电平标志传输结束。
5.控制信号en,什么时候工作
6.发送完成标志信号。
moudle框图
.v文件
- `timescale 1ns / 1ps
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/04/26 15:03:41
- // Design Name:
- // Module Name: UART_TX
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
-
- module UART_TX(
- Data,
- Send_en,
- Clk,
- Reset_n,
- Uart_tx,
- Tx_Done,
- Baud_set
- );
-
- input [7:0] Data;
- input Send_en;
- input Clk;
- input Reset_n;
- input [2:0] Baud_set;
- //B_S = 0 波特率9600 计数周期 104167ns 50Mhz计数值 5208-1
- //B_S = 1 波特率19200计数周期 52083ns 50Mhz计数值 2604-1
- //B_S = 2 波特率38400计数周期 26041ns 50Mhz计数值 1302-1
- //B_S = 3 波特率57600计数周期 17361ns 50Mhz计数值 868-1
- //B_S = 4 波特率115200计数周期 8680ns 50Mhz计数值 434-1
- output reg Uart_tx;
- output reg Tx_Done;
-
- /* 若波特率115200,那么1bit需要的时间是1s/115200=8680ns,
- 对于50Mhz主频(20ns周期)的时钟源,计数434(9位)次为1bit 持续时间 */
- /* 300波特率 1s/300=3333330ns,计数3333330/20=166666次(18位) */
- reg [17:0] div_cnt;//用于得到波特率的基本时钟
- reg [17:0] bps_DR;//不同波特率对应的计数最大值
- wire bps_clk;
- assign bps_clk = (div_cnt == 1);
-
-
- always@(*)
- case(Baud_set)
- 0:bps_DR = 5208;
- 1:bps_DR = 2640;
- 2:bps_DR = 1302;
- 3:bps_DR = 868;
- 4:bps_DR = 434;
- default:bps_DR = 5208;
- endcase
-
- always@(posedge Clk or negedge Reset_n)//基本时钟设置(1bit)
- if(!Reset_n)
- div_cnt <=0;
- else if(Send_en)
- if(div_cnt == bps_DR-1)
- div_cnt <=0;
- else
- div_cnt <= div_cnt + 1'b1;
- else
- div_cnt <= 0;
- //1 byte数据时钟
- reg [3:0] bps_cnt;
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- bps_cnt = 0;
- else if(Send_en) begin
- if(bps_clk)begin
- if(bps_cnt == 11)
- bps_cnt <= 0 ;
- else
- bps_cnt <= bps_cnt + 1'b1;
- end
- end
- else
- bps_cnt <= 0 ;
-
-
- 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)
- 1: begin Uart_tx <= 0; Tx_Done <= 1'b0;end
- 2: Uart_tx <= Data[0];
- 3: Uart_tx <= Data[1];
- 4: Uart_tx <= Data[2];
- 5: Uart_tx <= Data[3];
- 6: Uart_tx <= Data[4];
- 7: Uart_tx <= Data[5];
- 8: Uart_tx <= Data[6];
- 9: Uart_tx <= Data[7];
- 10:Uart_tx <= 1;
- 11:begin Uart_tx <= 1; Tx_Done <= 1'b1;end
- default:Uart_tx <= 1;
- endcase
- end
-
-
-
-
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
tb
文件
- `timescale 1ns / 1ps
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/04/26 17:53:58
- // Design Name:
- // Module Name: UART_TX_tb
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
-
- module UART_TX_tb(
-
- );
-
- reg [7:0] Data;
- reg Send_en;
- reg Clk;
- reg Reset_n;
- wire Uart_tx;
- wire Tx_Done;
- reg [2:0] Baud_set;
-
-
- UART_TX UART_TXinst(
- .Data(Data),
- .Send_en(Send_en),
- .Clk(Clk),
- .Reset_n(Reset_n),
- .Uart_tx(Uart_tx),
- .Tx_Done(Tx_Done),
- .Baud_set(Baud_set)
- );
-
- initial Clk = 1;
- always #10 Clk =!Clk;//clk产生时钟周期20ns 50Mhz
-
- initial begin
- Reset_n = 0;
- Data = 0;
- Send_en = 0;
- Baud_set = 4;
- #201
- Reset_n = 1;
- #100
- Data = 8'h57;
- Send_en = 1;
- #20;
- @(posedge Tx_Done);
- Send_en = 0;
- #20000;
- Data = 8'h75;
- Send_en = 1;
- #20;
- @(posedge Tx_Done);
- Send_en = 0;
- #20000
- $stop;
- end
-
-
-
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
基本逻辑
波形图
要求调用实验0模块实现10ms发送一个数据并且每次比上一次+1.
在上一次的工程里新建源文件test,置顶,模块中调用UART_TX,
.V文件
- `timescale 1ns / 1ps
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/04/28 11:35:47
- // Design Name:
- // Module Name: UART_TEST
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
- //设计要求,10ms发送一个数并且每次比上一次+1。
- module UART_TEST(
- Clk,
- Reset_n,
- Uart_tx
- );
-
- input Clk;
- input Reset_n;
- output Uart_tx;//子模块驱动的不要定义reg型。
- reg Send_en;
- reg [7:0] Data;
- /* reg Tx_Done; */
-
-
- UART_TX UART_byte_send(
- .Data(Data),
- .Send_en(Send_en),
- .Clk(Clk),
- .Reset_n(Reset_n),
- .Uart_tx(Uart_tx),
- .Tx_Done(Tx_Done),//子模块的输出信号可以直接调用,不需要在母模块中定义变量然后接入
- .Baud_set(3'd4)//子模块的入口可以接母模块的入口,也可以直接写死。
- );
- //计数器10ms 10ms/20ns=500000 19位
- reg [18:0] cnt;
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- cnt <= 0;
- else if(cnt == 500000 -1)
- cnt<=0;
- else
- cnt <= cnt + 1'b1;
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Send_en <= 0;
- else if(cnt == 1)
- Send_en <= 1;
- else if(Tx_Done)
- Send_en <= 0;
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Data <= 0;
- else if(Tx_Done)
- Data <= Data + 1'b1;
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
tb文件
-
- module UART_TX_tb(
-
- );
-
- reg Clk;
- reg Reset_n;
- wire Uart_tx;
-
-
- UART_TEST UART_TESTinst(
- .Clk(Clk),
- .Reset_n(Reset_n),
- .Uart_tx(Uart_tx)
- );
-
- initial Clk = 1;
- always #10 Clk =!Clk;//clk产生时钟周期20ns 50Mhz
-
- initial begin
- Reset_n = 0;
- #201
- Reset_n = 1;
- #50000000
- $stop;
- end
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
sim后发现发送波形不对
send en除了第一byte数据,剩下的都只持续了一个时钟周期,显然是不对的
data 自加也不对,正常应该是一个send周期结束后,data自加1
回到源代码找bug,关注data自加,senden拉高的条件
cnt ==1 senden拉高
没问题,再看senden拉低,是因为txdone =1,查看波形图之后发现问题,senden的时间段,txdone(发送结束标志)不应该是1,
因此推测是子模块中串口发送机制写错了,找关于txdone被拉低的部分。
bpscnt==1的时候 txdone拉低,但是由仿真图,,第一次发送结束之后,bpscnt并没有再次开启,一直都是0,也就导致了没有经过1
那么为什么bpscnt没有自加到1,代码显示senden和bpsclk同时为1时,bpscnt会自加
然而仿真图显示
senden和bpsclk没有同时为1的时刻,也就是说senden被提前拉低了,那么还是txdone == 1的问题,怎么能让txdone在发送结束之后不持续为1,修改代码后如下
重新仿真
txdone解决,但是data自加1不对
查看 data自加1的条件,发现是txdone == 1
再看txdone == 1 的波形图
持续了三个周期,导致每次都是data +3,为什么是3个时钟周期?如代码所示,div cnt==1的时候会导致bpsclk==1,下一拍导致bpscnt==11,下一拍导致txdone==1,下一拍导致senden=0,下一拍致bpscnt==0,下一拍导致bpscnt==0,下一拍txdone=0
找到了
持续三个时钟周期的原因,接下来换一下txdone的判断条件,由时序图可知,bpsclk=1并且bpscnt=10是一个很好的判断条件(只持续一个时钟周期,并且在串口结束信号发送完成之后)
修改代码,为了方便,将txdone单列一个always块,不会改变模块功能
解决
最后的.v文件
- module UART_TEST(
- Clk,
- Reset_n,
- Uart_tx
- );
-
- input Clk;
- input Reset_n;
- output Uart_tx;//子模块驱动的不要定义reg型。
- reg Send_en;
- reg [7:0] Data;
- /* reg Tx_Done; */
-
-
- UART_TX UART_byte_send(
- .Data(Data),
- .Send_en(Send_en),
- .Clk(Clk),
- .Reset_n(Reset_n),
- .Uart_tx(Uart_tx),
- .Tx_Done(Tx_Done),//子模块的输出信号可以直接调用,不需要在母模块中定义变量然后接入
- .Baud_set(3'd4)//子模块的入口可以接母模块的入口,也可以直接写死。
- );
- //计数器10ms 10ms/20ns=500000 19位
- reg [18:0] cnt;
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- cnt <= 0;
- else if(cnt == 500000 -1)
- cnt<=0;
- else
- cnt <= cnt + 1'b1;
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Send_en <= 0;
- else if(cnt == 1)
- Send_en <= 1;
- else if(Tx_Done)
- Send_en <= 0;
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Data <= 0;
- else if(Tx_Done)
- Data <= Data + 1'b1;//有问题
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
子模块文件
- module UART_TX(
- Data,
- Send_en,
- Clk,
- Reset_n,
- Uart_tx,
- Tx_Done,
- Baud_set
- );
-
- input [7:0] Data;
- input Send_en;
- input Clk;
- input Reset_n;
- input [2:0] Baud_set;
- //B_S = 0 波特率9600 计数周期 104167ns 50Mhz计数值 5208-1
- //B_S = 1 波特率19200计数周期 52083ns 50Mhz计数值 2604-1
- //B_S = 2 波特率38400计数周期 26041ns 50Mhz计数值 1302-1
- //B_S = 3 波特率57600计数周期 17361ns 50Mhz计数值 868-1
- //B_S = 4 波特率115200计数周期 8680ns 50Mhz计数值 434-1
- output reg Uart_tx;
- output reg Tx_Done;
-
- /* 若波特率115200,那么1bit需要的时间是1s/115200=8680ns,
- 对于50Mhz主频(20ns周期)的时钟源,计数434(9位)次为1bit 持续时间 */
- /* 300波特率 1s/300=3333330ns,计数3333330/20=166666次(18位) */
- reg [17:0] div_cnt;//用于得到波特率的基本时钟
- reg [17:0] bps_DR;//不同波特率对应的计数最大值
- wire bps_clk;
- assign bps_clk = (div_cnt == 1);
- always@(*)
- case(Baud_set)
- 0:bps_DR = 5208;
- 1:bps_DR = 2640;
- 2:bps_DR = 1302;
- 3:bps_DR = 868;
- 4:bps_DR = 434;
- default:bps_DR = 5208;
- endcase
-
- always@(posedge Clk or negedge Reset_n)//基本时钟设置(1bit)
- if(!Reset_n)
- div_cnt <=0;
- else if(Send_en)
- if(div_cnt == bps_DR-1)
- div_cnt <=0;
- else
- div_cnt <= div_cnt + 1'b1;
- else
- div_cnt <= 0;
- //1 byte数据时钟
- reg [3:0] bps_cnt;
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- bps_cnt = 0;
- else if(Send_en) begin
- if(bps_clk)begin
- if(bps_cnt == 11)
- bps_cnt <= 0 ;
- else
- bps_cnt <= bps_cnt + 1'b1;
- end
- end
- else
- bps_cnt <= 0 ;
-
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)begin
- Uart_tx <= 1'b1;
- end
- else begin
- case(bps_cnt)
- 1: Uart_tx <= 0;
- 2: Uart_tx <= Data[0];
- 3: Uart_tx <= Data[1];
- 4: Uart_tx <= Data[2];
- 5: Uart_tx <= Data[3];
- 6: Uart_tx <= Data[4];
- 7: Uart_tx <= Data[5];
- 8: Uart_tx <= Data[6];
- 9: Uart_tx <= Data[7];
- 10:Uart_tx <= 1;
- 11:Uart_tx <= 1;
- default:Uart_tx <= 1;
- endcase
- end
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Tx_Done <=0;
- else if((bps_clk == 1) && (bps_cnt == 10))
- Tx_Done <= 1;
- else
- Tx_Done <= 0;
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
进一步的,如果有某个其他的模块比如说adc,每次采集好了电压值以下图形式发送出去,那么send_en-txdone机制没有集成在UART_TX模块内会导致使用不方便,因此考虑在子模块内写如send_en与txdone的运行机制,外部给出一个接口send_go,当send_go信号发生时,自动发送1byte数据。
修改子模块将send_en-txdone机制集成在子模块内。
- module UART_TX(
- Data,
- Send_go,
- Clk,
- Reset_n,
- Uart_tx,
- Tx_Done,
- Baud_set
- );
-
- input [7:0] Data;
- input Send_go;
- input Clk;
- input Reset_n;
- input [2:0] Baud_set;
- //B_S = 0 波特率9600 计数周期 104167ns 50Mhz计数值 5208-1
- //B_S = 1 波特率19200计数周期 52083ns 50Mhz计数值 2604-1
- //B_S = 2 波特率38400计数周期 26041ns 50Mhz计数值 1302-1
- //B_S = 3 波特率57600计数周期 17361ns 50Mhz计数值 868-1
- //B_S = 4 波特率115200计数周期 8680ns 50Mhz计数值 434-1
- output reg Uart_tx;
- output reg Tx_Done;
-
- /* 若波特率115200,那么1bit需要的时间是1s/115200=8680ns,
- 对于50Mhz主频(20ns周期)的时钟源,计数434(9位)次为1bit 持续时间 */
- /* 300波特率 1s/300=3333330ns,计数3333330/20=166666次(18位) */
- reg [17:0] div_cnt;//用于得到波特率的基本时钟
- reg [17:0] bps_DR;//不同波特率对应的计数最大值
- wire bps_clk;
- assign bps_clk = (div_cnt == 1);
- always@(*)
- case(Baud_set)
- 0:bps_DR = 5208;
- 1:bps_DR = 2640;
- 2:bps_DR = 1302;
- 3:bps_DR = 868;
- 4:bps_DR = 434;
- default:bps_DR = 5208;
- endcase
-
- reg Send_en;
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Send_en <= 0;
- else if(Send_go)
- Send_en <= 1;
- else if(Tx_Done)
- Send_en <= 0;
-
- always@(posedge Clk or negedge Reset_n)//基本时钟设置(1bit)
- if(!Reset_n)
- div_cnt <=0;
- else if(Send_en)
- if(div_cnt == bps_DR-1)
- div_cnt <=0;
- else
- div_cnt <= div_cnt + 1'b1;
- else
- div_cnt <= 0;
- //1 byte数据时钟
- reg [3:0] bps_cnt;
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- bps_cnt = 0;
- else if(Send_en) begin
- if(bps_clk)begin
- if(bps_cnt == 11)
- bps_cnt <= 0 ;
- else
- bps_cnt <= bps_cnt + 1'b1;
- end
- end
- else
- bps_cnt <= 0 ;
-
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)begin
- Uart_tx <= 1'b1;
- end
- else begin
- case(bps_cnt)
- 1: Uart_tx <= 0;
- 2: Uart_tx <= Data[0];
- 3: Uart_tx <= Data[1];
- 4: Uart_tx <= Data[2];
- 5: Uart_tx <= Data[3];
- 6: Uart_tx <= Data[4];
- 7: Uart_tx <= Data[5];
- 8: Uart_tx <= Data[6];
- 9: Uart_tx <= Data[7];
- 10:Uart_tx <= 1;
- 11:Uart_tx <= 1;
- default:Uart_tx <= 1;
- endcase
- end
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Tx_Done <=0;
- else if((bps_clk == 1) && (bps_cnt == 10))
- Tx_Done <= 1;
- else
- Tx_Done <= 0;
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
这样设计还存在一个问题,当外部信号send_go使能串口发送模块之后,在模块发送数据时,data数组发生变化(假设从第一组电压值切换到了第二组电压值),那么data的后面几个时刻可能会发送第二组电压值的数据,这样会导致发出去的8bit数据是不同组数据的组合,原因是,子模块UART_TX的data接口是直接接入发送机制的,中间没有寄存器作缓冲。因此考虑在data与发送之间加入缓冲器。修改后子模块如下
- module UART_TX(
- Data,
- Send_go,
- Clk,
- Reset_n,
- Uart_tx,
- Tx_Done,
- Baud_set
- );
-
- input [7:0] Data;
- input Send_go;
- input Clk;
- input Reset_n;
- input [2:0] Baud_set;
- //B_S = 0 波特率9600 计数周期 104167ns 50Mhz计数值 5208-1
- //B_S = 1 波特率19200计数周期 52083ns 50Mhz计数值 2604-1
- //B_S = 2 波特率38400计数周期 26041ns 50Mhz计数值 1302-1
- //B_S = 3 波特率57600计数周期 17361ns 50Mhz计数值 868-1
- //B_S = 4 波特率115200计数周期 8680ns 50Mhz计数值 434-1
- output reg Uart_tx;
- output reg Tx_Done;
-
- /* 若波特率115200,那么1bit需要的时间是1s/115200=8680ns,
- 对于50Mhz主频(20ns周期)的时钟源,计数434(9位)次为1bit 持续时间 */
- /* 300波特率 1s/300=3333330ns,计数3333330/20=166666次(18位) */
- reg [17:0] div_cnt;//用于得到波特率的基本时钟
- reg [17:0] bps_DR;//不同波特率对应的计数最大值
- wire bps_clk;
- assign bps_clk = (div_cnt == 1);
- always@(*)
- case(Baud_set)
- 0:bps_DR = 5208;
- 1:bps_DR = 2640;
- 2:bps_DR = 1302;
- 3:bps_DR = 868;
- 4:bps_DR = 434;
- default:bps_DR = 5208;
- endcase
- reg [7:0] r_Data;
- always@(posedge Clk )
-
- if(Send_go)
- r_Data <= Data;
- else
- r_Data <= r_Data;
-
-
- reg Send_en;
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Send_en <= 0;
- else if(Send_go)
- Send_en <= 1;
- else if(Tx_Done)
- Send_en <= 0;
-
- always@(posedge Clk or negedge Reset_n)//基本时钟设置(1bit)
- if(!Reset_n)
- div_cnt <=0;
- else if(Send_en)
- if(div_cnt == bps_DR-1)
- div_cnt <=0;
- else
- div_cnt <= div_cnt + 1'b1;
- else
- div_cnt <= 0;
- //1 byte数据时钟
- reg [3:0] bps_cnt;
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- bps_cnt = 0;
- else if(Send_en) begin
- if(bps_clk)begin
- if(bps_cnt == 11)
- bps_cnt <= 0 ;
- else
- bps_cnt <= bps_cnt + 1'b1;
- end
- end
- else
- bps_cnt <= 0 ;
-
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)begin
- Uart_tx <= 1'b1;
- end
- else begin
- case(bps_cnt)
- 1: Uart_tx <= 0;
- 2: Uart_tx <= r_Data[0];
- 3: Uart_tx <= r_Data[1];
- 4: Uart_tx <= r_Data[2];
- 5: Uart_tx <= r_Data[3];
- 6: Uart_tx <= r_Data[4];
- 7: Uart_tx <= r_Data[5];
- 8: Uart_tx <= r_Data[6];
- 9: Uart_tx <= r_Data[7];
- 10:Uart_tx <= 1;
- 11:Uart_tx <= 1;
- default:Uart_tx <= 1;
- endcase
- end
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Tx_Done <=0;
- else if((bps_clk == 1) && (bps_cnt == 10))
- Tx_Done <= 1;
- else
- Tx_Done <= 0;
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
母模块
- `timescale 1ns / 1ps
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/04/28 11:35:47
- // Design Name:
- // Module Name: UART_TEST
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
- //设计要求,10ms发送一个数并且每次比上一次+1。
- module UART_TEST(
- Clk,
- Reset_n,
- Uart_tx,
- LED
- );
-
- input Clk;
- input Reset_n;
- output Uart_tx;//子模块驱动的不要定义reg型。
- output reg LED;
- reg Send_go;
- reg [7:0] Data;
- /* reg Tx_Done; */
- reg[25:0] cnt1;
- always@(posedge Clk or negedge Reset_n)//1s/20ns
- if(!Reset_n)
- cnt1 <= 0;
- else if(cnt1 == 50000000-1)
- cnt1 <= 0;
- else cnt1 <= cnt1 +1'b1;
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- LED <= 0;
- else if(cnt1 == 1)
- LED <= ~LED;
- else
- LED <= LED;
- UART_TX UART_byte_send(
- .Data(Data),
- .Send_go(Send_go),
- .Clk(Clk),
- .Reset_n(Reset_n),
- .Uart_tx(Uart_tx),
- .Tx_Done(Tx_Done),//子模块的输出信号可以直接调用,不需要在母模块中定义变量然后接入
- .Baud_set(3'd4)//子模块的入口可以接母模块的入口,也可以直接写死。
- );
-
-
- //计数器10ms 10ms/20ns=500000 19位
- reg [18:0] cnt;
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- cnt <= 0;
- else if(cnt == 500000 -1)
- cnt<=0;
- else
- cnt <= cnt + 1'b1;
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Send_go <= 0;
- else if(cnt == 1)
- Send_go <= 1;
- else
- Send_go <= 0;
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Data <= 0;
- else if(Tx_Done)
- Data <= Data + 1'b1;//有问题
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
采用状态机实现多字节发送
1.非状态机
母模块.v
- `timescale 1ns / 1ps
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/05/03 15:47:30
- // Design Name:
- // Module Name: uart_tx_data
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
-
- module uart_tx_data(
- Clk,
- Reset_n,
- Data40,//40位数据输入
- Trans_go,//40位数据发送信号
- Uart_tx,
- Trans_down
- );
-
- input Clk;
- input Reset_n;
- input [39:0] Data40;
- input Trans_go;
- output Uart_tx;
- output reg Trans_down;
- reg [7:0] Data;
- reg Send_go;
- wire Tx_Done;
- reg [2:0] state;
-
- UART_TX UART_byte_send(
- .Data(Data),
- .Send_go(Send_go),
- .Clk(Clk),
- .Reset_n(Reset_n),
- .Uart_tx(Uart_tx),
- .Tx_Done(Tx_Done),//子模块的输出信号可以直接调用,不需要在母模块中定义变量然后接入
- .Baud_set(3'd4)//子模块的入口可以接母模块的入口,也可以直接写死。
- );
- always @(posedge Clk or negedge Reset_n) begin
- if(!Reset_n)begin
- state <= 0;
- Data <= 0;
- Send_go <= 0;
- Trans_down <= 0;
- end
- else if(state == 0)begin
- Trans_down <= 0;
- if(Trans_go)begin
- Data <= Data40[7:0];
- Send_go <= 1;
- state <=1;
- end
- else begin
- Data <= Data;
- Send_go <= 0;
- state <=0;
- end
- end
- else if(state == 1)begin
- if(Tx_Done)begin
- Data <= Data40[15:8];
- Send_go <= 1;
- state <= 2;
- end
- else begin
- Data <= Data;
- Send_go <= 0;
- state <=1;
- end
- end
- else if(state == 2)begin
- if(Tx_Done)begin
- Data <= Data40[23:16];
- Send_go <= 1;
- state <= 3;
- end
- else begin
- Data <= Data;
- Send_go <= 0;
- state <=2;
- end
- end
- else if(state == 3)begin
- if(Tx_Done)begin
- Data <= Data40[31:24];
- Send_go <= 1;
- state <= 4;
- end
- else begin
- Data <= Data;
- Send_go <= 0;
- state <=3;
- end
- end
- else if(state == 4)begin
- if(Tx_Done)begin
- Data <= Data40[39:32];
- Send_go <= 1;
- state <= 5;
- end
- else begin
- Data <= Data;
- Send_go <= 0;
- state <=4;
- end
- end
- else if(state == 5)begin
- if(Tx_Done)begin
- Trans_down <= 1;
- Send_go <= 0;
- state <= 0;
- end
- else begin
- Data <= Data;
- Send_go <= 0;
- state <=5;
- end
- end
-
- end
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
子模块.v
- `timescale 1ns / 1ps
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/04/26 15:03:41
- // Design Name:
- // Module Name: UART_TX
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
-
- module UART_TX(
- Data,
- Send_go,
- Clk,
- Reset_n,
- Uart_tx,
- Tx_Done,
- Baud_set
- );
-
- input [7:0] Data;
- input Send_go;
- input Clk;
- input Reset_n;
- input [2:0] Baud_set;
- //B_S = 0 波特率9600 计数周期 104167ns 50Mhz计数值 5208-1
- //B_S = 1 波特率19200计数周期 52083ns 50Mhz计数值 2604-1
- //B_S = 2 波特率38400计数周期 26041ns 50Mhz计数值 1302-1
- //B_S = 3 波特率57600计数周期 17361ns 50Mhz计数值 868-1
- //B_S = 4 波特率115200计数周期 8680ns 50Mhz计数值 434-1
- output reg Uart_tx;
- output reg Tx_Done;
-
- /* 若波特率115200,那么1bit需要的时间是1s/115200=8680ns,
- 对于50Mhz主频(20ns周期)的时钟源,计数434(9位)次为1bit 持续时间 */
- /* 300波特率 1s/300=3333330ns,计数3333330/20=166666次(18位) */
- reg [17:0] div_cnt;//用于得到波特率的基本时钟
- reg [17:0] bps_DR;//不同波特率对应的计数最大值
- wire bps_clk;
- assign bps_clk = (div_cnt == 1);
- always@(*)
- case(Baud_set)
- 0:bps_DR = 5208;
- 1:bps_DR = 2640;
- 2:bps_DR = 1302;
- 3:bps_DR = 868;
- 4:bps_DR = 434;
- default:bps_DR = 5208;
- endcase
- reg [7:0] r_Data;
- always@(posedge Clk )
-
- if(Send_go)
- r_Data <= Data;
- else
- r_Data <= r_Data;
-
-
- reg Send_en;
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Send_en <= 0;
- else if(Send_go)
- Send_en <= 1;
- else if(Tx_Done)
- Send_en <= 0;
-
- always@(posedge Clk or negedge Reset_n)//基本时钟设置(1bit)
- if(!Reset_n)
- div_cnt <=0;
- else if(Send_en)
- if(div_cnt == bps_DR-1)
- div_cnt <=0;
- else
- div_cnt <= div_cnt + 1'b1;
- else
- div_cnt <= 0;
- //1 byte数据时钟
- reg [3:0] bps_cnt;
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- bps_cnt = 0;
- else if(Send_en) begin
- if(bps_clk)begin
- if(bps_cnt == 11)
- bps_cnt <= 0 ;
- else
- bps_cnt <= bps_cnt + 1'b1;
- end
- end
- else
- bps_cnt <= 0 ;
-
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)begin
- Uart_tx <= 1'b1;
- end
- else begin
- case(bps_cnt)
- 1: Uart_tx <= 0;
- 2: Uart_tx <= r_Data[0];
- 3: Uart_tx <= r_Data[1];
- 4: Uart_tx <= r_Data[2];
- 5: Uart_tx <= r_Data[3];
- 6: Uart_tx <= r_Data[4];
- 7: Uart_tx <= r_Data[5];
- 8: Uart_tx <= r_Data[6];
- 9: Uart_tx <= r_Data[7];
- 10:Uart_tx <= 1;
- 11:Uart_tx <= 1;
- default:Uart_tx <= 1;
- endcase
- end
-
- always@(posedge Clk or negedge Reset_n)
- if(!Reset_n)
- Tx_Done <=0;
- else if((bps_clk == 1) && (bps_cnt == 10))
- Tx_Done <= 1;
- else
- Tx_Done <= 0;
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
tb
- `timescale 1ns / 1ps
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/05/03 18:31:58
- // Design Name:
- // Module Name: uart_tx_data_tb
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
-
- module uart_tx_data_tb(
-
- );
- reg Clk;
- reg Reset_n;
- reg [39:0] Data40;
- reg Trans_go;
- wire Uart_tx;
-
- uart_tx_data uart_tx_datainst(
- Clk,
- Reset_n,
- Data40,//40位数据输入
- Trans_go,//40位数据发送信号
- Uart_tx,
- Trans_down
- );
-
- initial Clk = 1;
- always #10 Clk = ~Clk;
-
- initial begin
- Reset_n = 0;
- Data40 = 0;
- Trans_go = 0;
- #201;
- Reset_n = 1;
- #200;
- Data40 = 40'h123456789a;
- Trans_go =1;
- #20;
- Trans_go = 0;
- @(posedge Trans_down);
- #200000;
- Data40 = 40'ha987654321;
- Trans_go =1;
- #20;
- Trans_go = 0;
- @(posedge Trans_down);
- #200000;
- $stop;
-
-
-
- end
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
2.状态机 将母模块ifelse修改为 case
- `timescale 1ns / 1ps
- //
- // Company:
- // Engineer:
- //
- // Create Date: 2024/05/03 18:56:20
- // Design Name:
- // Module Name: uart_tx_data2
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //
-
-
- module uart_tx_data2(
- Clk,
- Reset_n,
- Data40,//40位数据输入
- Trans_go,//40位数据发送信号
- Uart_tx,
- Trans_down
- );
-
- input Clk;
- input Reset_n;
- input [39:0] Data40;
- input Trans_go;
- output Uart_tx;
- output reg Trans_down;
- reg [7:0] Data;
- reg Send_go;
- wire Tx_Done;
- reg [2:0] state;
-
- UART_TX UART_byte_send(
- .Data(Data),
- .Send_go(Send_go),
- .Clk(Clk),
- .Reset_n(Reset_n),
- .Uart_tx(Uart_tx),
- .Tx_Done(Tx_Done),//子模块的输出信号可以直接调用,不需要在母模块中定义变量然后接入
- .Baud_set(3'd4)//子模块的入口可以接母模块的入口,也可以直接写死。
- );
- always @(posedge Clk or negedge Reset_n) begin
- if(!Reset_n)begin
- state <= 0;
- Data <= 0;
- Send_go <= 0;
- Trans_down <= 0;
- end
- else begin
- case(state)
- 0:
- begin
- Trans_down <= 0;
- if(Trans_go)begin
- Data <= Data40[7:0];
- Send_go <= 1;
- state <=1;
- end
- else begin
- Data <= Data;
- Send_go <= 0;
- state <=0;
- end
- end
- 1:
- begin
- if(Tx_Done)begin
- Data <= Data40[15:8];
- Send_go <= 1;
- state <= 2;
- end
- else begin
- Data <= Data;
- Send_go <= 0;
- state <=1;
- end
- end
- 2:
- begin
- if(Tx_Done)begin
- Data <= Data40[23:16];
- Send_go <= 1;
- state <= 3;
- end
- else begin
- Data <= Data;
- Send_go <= 0;
- state <=2;
- end
- end
- 3:
- begin
- if(Tx_Done)begin
- Data <= Data40[31:24];
- Send_go <= 1;
- state <= 4;
- end
- else begin
- Data <= Data;
- Send_go <= 0;
- state <=3;
- end
- end
- 4:
- begin
- if(Tx_Done)begin
- Data <= Data40[39:32];
- Send_go <= 1;
- state <= 5;
- end
- else begin
- Data <= Data;
- Send_go <= 0;
- state <=4;
- end
- end
- 5:
- begin
- if(Tx_Done)begin
- Trans_down <= 1;
- Send_go <= 0;
- state <= 0;
- end
- else begin
- Data <= Data;
- Send_go <= 0;
- state <=5;
- end
- end
- default:
- begin
- Data <= Data;
- Send_go <= 0;
- state <=0;
- end
- endcase
-
- end
- end
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。