当前位置:   article > 正文

zynq学习笔记3,fpga开发流程(基于vivado) testbench_vivado testbench

vivado testbench

目标:写一套硬件描述语言,能够在指定平台实现相应功能

1.设计定义(led一秒闪烁一次)

2.设计输入(编写逻辑verilog描述,画逻辑图,使用ip核(例如调用厂家写好的fft))

3.综合工具(由专业的EDA提供,vivado,quartus),对所写的逻辑进行分析综合,得到逻辑门级别的电路内容RTL

4.功能仿真(使用专门的仿真工具modelsim进行仿真,验证设计的逻辑功能能够实现,仿真基本接近真实情况,可信度比较高)

5.布局布线,在指定的器件上将设计的逻辑电路实现,布线不合理主频低。

6.分析性能(vivado)

1).时序仿真,布线后可进行时序仿真,看输入输出的延迟能不能达到要求,非常耗时

2).静态时序分析,常用。

性能不满足回到设计输入修改,

7.下载到目标板上运行,查看运行结果。ILA,signaltap调试

让设计的逻辑在目标板上正常工作。

mux2二选一电路 testbench编写

1.源文件如下

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/04/20 15:42:23
  7. // Design Name:
  8. // Module Name: xmgmux2
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module xmgmux2(
  22. a,
  23. b,
  24. sel,
  25. out
  26. );
  27. input a;
  28. input b;
  29. input sel;
  30. output out;
  31. assign out = (sel==1)?a:b;
  32. endmodule

 2.写好源文件后runsynthesis看看有没有语法错误

3.编写测试文件,测试文件的功能是给设计好的模块,施加激励,使之有输出,然后查看输入输出是否符合设计要求。

 tb文件如下

  1. `timescale 1ns / 1ps//1ns时间的步进
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/04/20 15:53:53
  7. // Design Name:
  8. // Module Name: mux2_tb
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module mux2_tb(
  22. //测试文件不需要端口
  23. );
  24. reg s_a;//激励定义成reg型
  25. reg s_b;
  26. reg sel;
  27. wire out;//输出接口定义成wire型
  28. xmgmux2 mux2inst0(
  29. .a(s_a),
  30. .b(s_b),
  31. .sel(sel),
  32. .out(out)
  33. );
  34. initial begin
  35. s_a = 0;
  36. s_b = 0;
  37. sel = 0;
  38. #200;
  39. s_a = 0;
  40. s_b = 0;
  41. sel = 1;
  42. #200;
  43. s_a = 0;
  44. s_b = 1;
  45. sel = 0;
  46. #200;
  47. s_a = 0;
  48. s_b = 1;
  49. sel = 1;
  50. #200;
  51. s_a = 1;
  52. s_b = 0;
  53. sel = 0;
  54. #200;
  55. s_a = 1;
  56. s_b = 0;
  57. sel = 1;
  58. #200;
  59. s_a = 1;
  60. s_b = 1;
  61. sel = 0;
  62. #200;
  63. s_a = 1;
  64. s_b = 1;
  65. sel = 1;
  66. #200;
  67. end
  68. endmodule

 总结来说就是 设计几个激励源接入到模块中

之后调用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.生成比特流文件。下载

计数器 led闪烁 简单的时序逻辑电路

计数器原理

源代码

  1. module cnt_led_flash(
  2. led,
  3. rst_n,
  4. clk
  5. );
  6. input clk;
  7. input rst_n;
  8. output reg led;
  9. reg [24:0] cnt;
  10. //<= 非阻塞赋值 暂时理解为“=
  11. /* always@(posedge clk or negedge rst_n)
  12. if(!rst_n)begin
  13. cnt <= 0;
  14. led <=0;
  15. end
  16. else if(cnt == 25000000)begin
  17. led <= !led;//若要在always块中赋值led 则led必须是reg型
  18. cnt <= 0;
  19. end
  20. else
  21. cnt <= cnt + 1'd1;
  22. */
  23. /* led和cnt分开写有助于编译器生成更简洁的逻辑门电路 */
  24. always@(posedge clk or negedge rst_n)
  25. if(!rst_n)
  26. cnt <= 0;
  27. else if(cnt == 24999999)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
  28. cnt <= 0;
  29. else
  30. cnt <= cnt + 1'd1;
  31. always@(posedge clk or negedge rst_n)
  32. if(!rst_n)
  33. led <=0;
  34. else if(cnt == 24999999)
  35. led <= !led;//若要在always块中赋值led 则led必须是reg型
  36. endmodule

 之后编写testbench

  1. `timescale 1ns / 1ns
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/04/22 20:23:37
  7. // Design Name:
  8. // Module Name: cnt_led_flash_tb
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module cnt_led_flash_tb(
  22. //无需输入输出端
  23. );
  24. //定义激励信号
  25. reg clk;
  26. reg rst;
  27. wire led;
  28. /* 将激励信号接入模块 */
  29. cnt_led_flash cnt_led_flashinst0(
  30. .led(led),
  31. .rst_n(rst),
  32. .clk(clk)
  33. );
  34. /* 产生激励信号的逻辑 */
  35. initial clk = 1;
  36. always #10 clk = !clk;//clk生成了周期10ns的时钟信号
  37. initial begin
  38. rst = 0;
  39. #201;//时钟沿之后一点点
  40. rst = 1;
  41. #2000000000;
  42. $stop;
  43. end
  44. endmodule

保存后 run simulation,可能会持续很久,因为需要24999999个上升沿

上升沿间隔500ms

之后上板验证

跑马灯移位或者位拼接

移位操作跑马灯 源文件

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/04/23 11:26:25
  7. // Design Name:
  8. // Module Name: led_run
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module led_run(
  22. rst_n,
  23. clk,
  24. led
  25. );
  26. input rst_n;
  27. input clk;
  28. output reg [7:0] led;//八位跑马灯 在always赋值,reg型
  29. reg [24:0] cnt;//500毫秒延时 25
  30. always@(posedge clk or negedge rst_n)
  31. if(!rst_n)//复位就是复位,不要和后面的操作合并如if((!rst_n)||(cnt == 25000000-1)),哪怕逻辑一样也不行
  32. cnt <= 0;
  33. else if(cnt == 25000-1)//时间缩短,方便调试
  34. /* else if(cnt == 25000000-1) */
  35. cnt <= 0;
  36. else
  37. cnt <= cnt +1'b1;//赋值要加位宽限定,判断的时候可以不加
  38. //移位法实现跑马灯 0000 0001→0000 0010......
  39. always@(posedge clk or negedge rst_n)
  40. if(!rst_n)
  41. led <= 8'b0000_0001;
  42. else if(cnt == 25000-1)begin//只有一句不用begin——end,多句需要begin——end
  43. if(led == 8'b1000_0000)
  44. led <= 8'b0000_0001;
  45. else
  46. led <= led << 1;
  47. end
  48. else
  49. led <= led;//写不写都行,时序逻辑默认保持状态
  50. endmodule

tb文件

  1. `timescale 1ns / 1ns
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/04/23 11:49:21
  7. // Design Name:
  8. // Module Name: led_run_tb
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module led_run_tb(
  22. //仿真不需要输入输出端口
  23. );
  24. reg rst_n;
  25. reg clk;
  26. wire [7:0] led;
  27. /* 将激励输出接口接入模块 */
  28. led_run led_runinst0(
  29. .rst_n(rst_n),
  30. .clk(clk),
  31. .led(led)
  32. );
  33. //编写激励逻辑
  34. initial clk = 1;
  35. always #10 clk =!clk;//clk产生时钟周期
  36. initial begin
  37. rst_n = 0;
  38. #201;
  39. rst_n = 1;
  40. #4000000;//延时五秒
  41. $stop;
  42. end
  43. endmodule

位拼接操作跑马灯 源文件

原理如下

在工程中某个.v文件中调用其他.v文件的模块

跑马灯中调用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型

 Verilog语法 参数重定义

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文件中。

从计数器到可控线性序列机

1. 让led亮0.25灭0.75

亮0.5灭0.5设计思想:之前是计数器计数满(0.5s)led反转; 另一种设计思路,计数器记满1s(49999999)归零,其中cnt=24999999(MCNT/2-1)高电平,cnt=49999999低电平

  1. //0.50.5
  2. module led_evo_1(
  3. clk,
  4. rst_n,
  5. led
  6. );
  7. input clk;
  8. input rst_n;
  9. output reg led;
  10. reg [25:0] cnt;//计数1s
  11. parameter MCNT = 50000000;
  12. //<= 非阻塞赋值 暂时理解为“=
  13. /* led和cnt分开写有助于编译器生成更简洁的逻辑门电路 */
  14. always@(posedge clk or negedge rst_n )
  15. if(!rst_n)
  16. cnt <= 0;
  17. else if(cnt == MCNT-1)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
  18. cnt <= 0;
  19. else
  20. cnt <= cnt + 1'd1;
  21. always@(posedge clk or negedge rst_n)
  22. if(!rst_n)
  23. led <=0;
  24. else if(cnt == MCNT/2 - 1)
  25. led <= 1;//若要在always块中赋值led 则led必须是reg型
  26. else if(cnt == MCNT-1)
  27. led <= 0;
  28. endmodule

由此可以完成亮0.25灭0.75,只需要将亮的时间改一下

结果如下

2. 亮0.25灭0.5亮0.75灭1S

思路,计数周期2.5s 其中0-0.25亮 0.25-0.75s灭 0.75-1.5亮 1.5-2.5灭

tb

  1. module led_evo_1_tb(
  2. //仿真不需要输入输出端口
  3. );
  4. reg rst_n;
  5. reg clk;
  6. wire led;
  7. /* 将激励输出接口接入模块 */
  8. led_evo_2
  9. #(.MCNT(125000))
  10. led_evo_2inst0(
  11. .clk(clk),
  12. .rst_n(rst_n),
  13. .led(led)
  14. );
  15. //编写激励逻辑
  16. initial clk = 1;
  17. always #10 clk =!clk;//clk产生时钟周期
  18. initial begin
  19. rst_n = 0;
  20. #201;
  21. rst_n = 1;
  22. #2000000000;//延时2
  23. $stop;
  24. end
  25. endmodule

3.指定模式亮灭,亮灭模式未知由用户随机指定。以0.25s为一个变化周期,八个变化状态为一个循环。

设计思路;定义一个8位的变量[7:0],每一位储存一个状态,然后将计数周期(2s)分为八份(0.25)每份对应变量的每一位

.V

tb

结果

其中scope可以查看文件中变量的值

总结:计数器的计数值可以作刻度标尺
tips:verilog尽量不要用连续的ifelse,可以用case代替

 4.指定模式亮灭,亮灭模式未知由用户随机指定。八个变化状态为一个循环,每个变化状态的时间值可任意设置。

思路,两个计数器,一个计数器计数基本时间(一个状态),第二个计数器数八个基本事件单位

.v

  1. module led_evo_4(
  2. clk,
  3. rst_n,
  4. ctrl,
  5. Time,
  6. led
  7. );
  8. input clk;
  9. input rst_n;
  10. input [7:0] ctrl;
  11. input [31:0] Time;
  12. output reg led;
  13. reg [31:0] cnt;//计数2s
  14. /* parameter MCNT = 100000000;//2s/20ns=100000000 */
  15. //<= 非阻塞赋值 暂时理解为“=
  16. /* led和cnt分开写有助于编译器生成更简洁的逻辑门电路 */
  17. always@(posedge clk or negedge rst_n )
  18. if(!rst_n)
  19. cnt <= 0;
  20. else if(cnt == Time-1)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
  21. cnt <= 0;
  22. else
  23. cnt <= cnt + 1'd1;
  24. reg [2:0] cnt2;
  25. always@(posedge clk or negedge rst_n )
  26. if(!rst_n)
  27. cnt2 <= 0;
  28. else if(cnt == Time-1)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
  29. cnt2 <= cnt2 + 1'b1;
  30. always@(posedge clk or negedge rst_n)
  31. if(!rst_n)
  32. led <=0;
  33. else case (cnt2)
  34. 0 : led <= ctrl[0];
  35. 1 : led <= ctrl[1];
  36. 2 : led <= ctrl[2];
  37. 3 : led <= ctrl[3];
  38. 4 : led <= ctrl[4];
  39. 5 : led <= ctrl[5];
  40. 6 : led <= ctrl[6];
  41. 7 : led <= ctrl[7];
  42. default: led<=led;//其他情况保持原值不变
  43. endcase
  44. endmodule

tb

  1. module led_evo_1_tb(
  2. //仿真不需要输入输出端口
  3. );
  4. reg rst_n;
  5. reg [7:0] ctrl;
  6. reg clk;
  7. reg [31:0] Time;
  8. wire led;
  9. /* 将激励输出接口接入模块 */
  10. led_evo_4 led_evo_4inst0(
  11. .clk(clk),
  12. .rst_n(rst_n),
  13. .ctrl(ctrl),
  14. .Time(Time),
  15. .led(led)
  16. );
  17. //编写激励逻辑
  18. initial clk = 1;
  19. always #10 clk =!clk;//clk产生时钟周期
  20. initial begin
  21. rst_n = 0;
  22. ctrl = 0;
  23. Time = 0;
  24. #201;
  25. rst_n = 1;
  26. #2000;
  27. Time = 2500;
  28. ctrl = 8'b1000_0001;
  29. #2000000000;//延时2秒
  30. $stop;
  31. end
  32. endmodule

调节time以及ctrl即可

5.多个led按照设置的模式各自在一个变化循环内独立亮灭

思路:多个ctrl[7:0]控制不同led的一字节亮灭

.v

  1. module led_evo_5(
  2. clk,
  3. rst_n,
  4. ctrlA,
  5. ctrlB,
  6. Time,
  7. led
  8. );
  9. input clk;
  10. input rst_n;
  11. input [7:0] ctrlA;
  12. input [7:0] ctrlB;
  13. input [31:0] Time;
  14. output reg [1:0] led;
  15. reg [31:0] cnt;
  16. always@(posedge clk or negedge rst_n )
  17. if(!rst_n)
  18. cnt <= 0;
  19. else if(cnt == Time-1)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
  20. cnt <= 0;
  21. else
  22. cnt <= cnt + 1'd1;
  23. reg [2:0] cnt2;
  24. always@(posedge clk or negedge rst_n )
  25. if(!rst_n)
  26. cnt2 <= 0;
  27. else if(cnt == Time-1)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
  28. cnt2 <= cnt2 + 1'b1;
  29. always@(posedge clk or negedge rst_n)
  30. if(!rst_n)
  31. led <=0;
  32. else case (cnt2)
  33. 0 : begin led[0] <= ctrlA[0];led[1] <= ctrlB[0] ;end
  34. 1 : begin led[0] <= ctrlA[1];led[1] <= ctrlB[1] ;end
  35. 2 : begin led[0] <= ctrlA[2];led[1] <= ctrlB[2] ;end
  36. 3 : begin led[0] <= ctrlA[3];led[1] <= ctrlB[3] ;end
  37. 4 : begin led[0] <= ctrlA[4];led[1] <= ctrlB[4] ;end
  38. 5 : begin led[0] <= ctrlA[5];led[1] <= ctrlB[5] ;end
  39. 6 : begin led[0] <= ctrlA[6];led[1] <= ctrlB[6] ;end
  40. 7 : begin led[0] <= ctrlA[7];led[1] <= ctrlB[7] ;end
  41. default: led<=led;//其他情况保持原值不变
  42. endcase
  43. endmodule

tb

  1. module led_evo_1_tb(
  2. //仿真不需要输入输出端口
  3. );
  4. reg rst_n;
  5. reg [7:0] ctrlA;
  6. reg [7:0] ctrlB;
  7. reg clk;
  8. reg [31:0] Time;
  9. wire [1:0] led;
  10. /* 将激励输出接口接入模块 */
  11. led_evo_5 led_evo_5inst0(
  12. .clk(clk),
  13. .rst_n(rst_n),
  14. .ctrlA(ctrlA),
  15. .ctrlB(ctrlB),
  16. .Time(Time),
  17. .led(led)
  18. );
  19. //编写激励逻辑
  20. initial clk = 1;
  21. always #10 clk =!clk;//clk产生时钟周期20ns 50Mhz
  22. initial begin
  23. rst_n = 0;
  24. ctrlA = 0;
  25. ctrlB = 0;
  26. Time = 0;
  27. #201;
  28. rst_n = 1;
  29. #2000;
  30. Time = 2500;
  31. ctrlA = 8'b1000_0001;
  32. ctrlB =8'b0111_1110;
  33. #2000000000;//延时2
  34. $stop;
  35. end
  36. endmodule

结果

6. 10ms执行一次8个状态

 思路控制cnt0每10ms计数一次 添加EN参数,EN高电平时开始产生状态序列

.v文件

  1. module led_evo_6(
  2. clk,
  3. rst_n,
  4. ctrla,
  5. timea,
  6. led
  7. );
  8. input clk;
  9. input rst_n;
  10. input [7:0] ctrla;
  11. input [31:0] timea;
  12. output reg led;
  13. reg EN;
  14. reg [18:0] cnt0;//计数10ms]
  15. reg [2:0] cnt2;
  16. reg [31:0] cnt1;
  17. //10ms计数器
  18. always@(posedge clk or negedge rst_n)
  19. if(!rst_n)
  20. cnt0 <= 0;
  21. else if(cnt0 == 500000-1)//计数10ms
  22. cnt0 <= 0;
  23. else
  24. cnt0 <= cnt0 + 1'b1;
  25. always@(posedge clk or negedge rst_n)
  26. if(!rst_n)
  27. EN <= 0;
  28. else if(cnt0 == 0)
  29. EN <= 1;
  30. else if (cnt2 == 7 && cnt1 == timea -1)
  31. EN <= 0;
  32. //timea计数器
  33. always@(posedge clk or negedge rst_n )//led0一个状态持续时间
  34. if(!rst_n)
  35. cnt1 <= 0;
  36. else if(EN)begin
  37. if(cnt1 == timea-1)
  38. cnt1 <= 0;
  39. else
  40. cnt1 <= cnt1 + 1'd1;
  41. end
  42. else cnt1 <= 0;
  43. always@(posedge clk or negedge rst_n )//led0 8个状态
  44. if(!rst_n)
  45. cnt2 <= 0;
  46. else if(EN)begin
  47. if(cnt1 == timea-1)//0-1 1-2 2-3 3-4 4-0 一共计数了5次,因此要填n-1.
  48. cnt2 <= cnt2 + 1'b1;
  49. end
  50. else
  51. cnt2 <= 0;
  52. always@(posedge clk or negedge rst_n)
  53. if(!rst_n)
  54. led <=0;
  55. else case (cnt2)
  56. 0 : led <= ctrla[0];
  57. 1 : led <= ctrla[1];
  58. 2 : led <= ctrla[2];
  59. 3 : led <= ctrla[3];
  60. 4 : led <= ctrla[4];
  61. 5 : led <= ctrla[5];
  62. 6 : led <= ctrla[6];
  63. 7 : led <= ctrla[7];
  64. default: led<=led;//其他情况保持原值不变
  65. endcase
  66. endmodule

tb文件

  1. module led_evo_1_tb(
  2. //仿真不需要输入输出端口
  3. );
  4. reg rst_n;
  5. reg [7:0] ctrlA;
  6. reg clk;
  7. reg [31:0] TIMEA;
  8. wire led;
  9. /* 将激励输出接口接入模块 */
  10. led_evo_6 led_evo_6inst0(
  11. .clk(clk),
  12. .rst_n(rst_n),
  13. .ctrla(ctrlA),
  14. .timea(TIMEA),
  15. .led(led)
  16. );
  17. //编写激励逻辑
  18. initial clk = 1;
  19. always #10 clk =!clk;//clk产生时钟周期20ns 50Mhz
  20. initial begin
  21. rst_n = 0;
  22. ctrlA = 0;
  23. TIMEA = 0;
  24. #201;
  25. rst_n = 1;
  26. #2000;
  27. TIMEA = 2500;
  28. ctrlA = 8'b1000_0110;
  29. #2000000000;//延时2秒
  30. $stop;
  31. end
  32. endmodule
tips 调试时可以将模块添加至窗口,查看模块内参数的变化,需要relaunch 不然没有参数波形

 总结,通过在计数器中添加EN标志,可以实现计数可控,EN高电平时开始计数,

阻塞与非阻塞赋值

阻塞赋值与非阻塞赋值的概念只在时序逻辑中才存在。

always@(*)

case(a,b,c)

0:out = 8'b00000001; 这段既不属于阻塞也不属于非阻塞

阻塞赋值写出来的电路,代码先后顺序不一样,综合出来的电路也可能不一样。

逻辑电路不同就会导致时序图有区别.

串口发送实验0

1.数据输入端口

2.波特率设置端口

3.八位并行数据TX线串行输出

4.串口通信以一位低电平标志传输开始,8位数据传输完成之后,一位高电平标志传输结束。

5.控制信号en,什么时候工作

6.发送完成标志信号。

moudle框图

.v文件

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/04/26 15:03:41
  7. // Design Name:
  8. // Module Name: UART_TX
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module UART_TX(
  22. Data,
  23. Send_en,
  24. Clk,
  25. Reset_n,
  26. Uart_tx,
  27. Tx_Done,
  28. Baud_set
  29. );
  30. input [7:0] Data;
  31. input Send_en;
  32. input Clk;
  33. input Reset_n;
  34. input [2:0] Baud_set;
  35. //B_S = 0 波特率9600 计数周期 104167ns 50Mhz计数值 5208-1
  36. //B_S = 1 波特率19200计数周期 52083ns 50Mhz计数值 2604-1
  37. //B_S = 2 波特率38400计数周期 26041ns 50Mhz计数值 1302-1
  38. //B_S = 3 波特率57600计数周期 17361ns 50Mhz计数值 868-1
  39. //B_S = 4 波特率115200计数周期 8680ns 50Mhz计数值 434-1
  40. output reg Uart_tx;
  41. output reg Tx_Done;
  42. /* 若波特率115200,那么1bit需要的时间是1s/115200=8680ns,
  43. 对于50Mhz主频(20ns周期)的时钟源,计数434(9位)次为1bit 持续时间 */
  44. /* 300波特率 1s/300=3333330ns,计数3333330/20=166666次(18位) */
  45. reg [17:0] div_cnt;//用于得到波特率的基本时钟
  46. reg [17:0] bps_DR;//不同波特率对应的计数最大值
  47. wire bps_clk;
  48. assign bps_clk = (div_cnt == 1);
  49. always@(*)
  50. case(Baud_set)
  51. 0:bps_DR = 5208;
  52. 1:bps_DR = 2640;
  53. 2:bps_DR = 1302;
  54. 3:bps_DR = 868;
  55. 4:bps_DR = 434;
  56. default:bps_DR = 5208;
  57. endcase
  58. always@(posedge Clk or negedge Reset_n)//基本时钟设置(1bit)
  59. if(!Reset_n)
  60. div_cnt <=0;
  61. else if(Send_en)
  62. if(div_cnt == bps_DR-1)
  63. div_cnt <=0;
  64. else
  65. div_cnt <= div_cnt + 1'b1;
  66. else
  67. div_cnt <= 0;
  68. //1 byte数据时钟
  69. reg [3:0] bps_cnt;
  70. always@(posedge Clk or negedge Reset_n)
  71. if(!Reset_n)
  72. bps_cnt = 0;
  73. else if(Send_en) begin
  74. if(bps_clk)begin
  75. if(bps_cnt == 11)
  76. bps_cnt <= 0 ;
  77. else
  78. bps_cnt <= bps_cnt + 1'b1;
  79. end
  80. end
  81. else
  82. bps_cnt <= 0 ;
  83. always@(posedge Clk or negedge Reset_n)
  84. if(!Reset_n)begin
  85. Uart_tx <= 1'b1;
  86. Tx_Done <= 1'b0;
  87. end
  88. else begin
  89. case(bps_cnt)
  90. 1: begin Uart_tx <= 0; Tx_Done <= 1'b0;end
  91. 2: Uart_tx <= Data[0];
  92. 3: Uart_tx <= Data[1];
  93. 4: Uart_tx <= Data[2];
  94. 5: Uart_tx <= Data[3];
  95. 6: Uart_tx <= Data[4];
  96. 7: Uart_tx <= Data[5];
  97. 8: Uart_tx <= Data[6];
  98. 9: Uart_tx <= Data[7];
  99. 10:Uart_tx <= 1;
  100. 11:begin Uart_tx <= 1; Tx_Done <= 1'b1;end
  101. default:Uart_tx <= 1;
  102. endcase
  103. end
  104. endmodule

tb

文件

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/04/26 17:53:58
  7. // Design Name:
  8. // Module Name: UART_TX_tb
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module UART_TX_tb(
  22. );
  23. reg [7:0] Data;
  24. reg Send_en;
  25. reg Clk;
  26. reg Reset_n;
  27. wire Uart_tx;
  28. wire Tx_Done;
  29. reg [2:0] Baud_set;
  30. UART_TX UART_TXinst(
  31. .Data(Data),
  32. .Send_en(Send_en),
  33. .Clk(Clk),
  34. .Reset_n(Reset_n),
  35. .Uart_tx(Uart_tx),
  36. .Tx_Done(Tx_Done),
  37. .Baud_set(Baud_set)
  38. );
  39. initial Clk = 1;
  40. always #10 Clk =!Clk;//clk产生时钟周期20ns 50Mhz
  41. initial begin
  42. Reset_n = 0;
  43. Data = 0;
  44. Send_en = 0;
  45. Baud_set = 4;
  46. #201
  47. Reset_n = 1;
  48. #100
  49. Data = 8'h57;
  50. Send_en = 1;
  51. #20;
  52. @(posedge Tx_Done);
  53. Send_en = 0;
  54. #20000;
  55. Data = 8'h75;
  56. Send_en = 1;
  57. #20;
  58. @(posedge Tx_Done);
  59. Send_en = 0;
  60. #20000
  61. $stop;
  62. end
  63. endmodule

基本逻辑

 波形图

串口发送实验1

要求调用实验0模块实现10ms发送一个数据并且每次比上一次+1.

在上一次的工程里新建源文件test,置顶,模块中调用UART_TX,

.V文件

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/04/28 11:35:47
  7. // Design Name:
  8. // Module Name: UART_TEST
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. //设计要求,10ms发送一个数并且每次比上一次+1
  22. module UART_TEST(
  23. Clk,
  24. Reset_n,
  25. Uart_tx
  26. );
  27. input Clk;
  28. input Reset_n;
  29. output Uart_tx;//子模块驱动的不要定义reg型。
  30. reg Send_en;
  31. reg [7:0] Data;
  32. /* reg Tx_Done; */
  33. UART_TX UART_byte_send(
  34. .Data(Data),
  35. .Send_en(Send_en),
  36. .Clk(Clk),
  37. .Reset_n(Reset_n),
  38. .Uart_tx(Uart_tx),
  39. .Tx_Done(Tx_Done),//子模块的输出信号可以直接调用,不需要在母模块中定义变量然后接入
  40. .Baud_set(3'd4)//子模块的入口可以接母模块的入口,也可以直接写死。
  41. );
  42. //计数器10ms 10ms/20ns=500000 19位
  43. reg [18:0] cnt;
  44. always@(posedge Clk or negedge Reset_n)
  45. if(!Reset_n)
  46. cnt <= 0;
  47. else if(cnt == 500000 -1)
  48. cnt<=0;
  49. else
  50. cnt <= cnt + 1'b1;
  51. always@(posedge Clk or negedge Reset_n)
  52. if(!Reset_n)
  53. Send_en <= 0;
  54. else if(cnt == 1)
  55. Send_en <= 1;
  56. else if(Tx_Done)
  57. Send_en <= 0;
  58. always@(posedge Clk or negedge Reset_n)
  59. if(!Reset_n)
  60. Data <= 0;
  61. else if(Tx_Done)
  62. Data <= Data + 1'b1;
  63. endmodule

tb文件

  1. module UART_TX_tb(
  2. );
  3. reg Clk;
  4. reg Reset_n;
  5. wire Uart_tx;
  6. UART_TEST UART_TESTinst(
  7. .Clk(Clk),
  8. .Reset_n(Reset_n),
  9. .Uart_tx(Uart_tx)
  10. );
  11. initial Clk = 1;
  12. always #10 Clk =!Clk;//clk产生时钟周期20ns 50Mhz
  13. initial begin
  14. Reset_n = 0;
  15. #201
  16. Reset_n = 1;
  17. #50000000
  18. $stop;
  19. end

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文件

  1. module UART_TEST(
  2. Clk,
  3. Reset_n,
  4. Uart_tx
  5. );
  6. input Clk;
  7. input Reset_n;
  8. output Uart_tx;//子模块驱动的不要定义reg型。
  9. reg Send_en;
  10. reg [7:0] Data;
  11. /* reg Tx_Done; */
  12. UART_TX UART_byte_send(
  13. .Data(Data),
  14. .Send_en(Send_en),
  15. .Clk(Clk),
  16. .Reset_n(Reset_n),
  17. .Uart_tx(Uart_tx),
  18. .Tx_Done(Tx_Done),//子模块的输出信号可以直接调用,不需要在母模块中定义变量然后接入
  19. .Baud_set(3'd4)//子模块的入口可以接母模块的入口,也可以直接写死。
  20. );
  21. //计数器10ms 10ms/20ns=500000 19位
  22. reg [18:0] cnt;
  23. always@(posedge Clk or negedge Reset_n)
  24. if(!Reset_n)
  25. cnt <= 0;
  26. else if(cnt == 500000 -1)
  27. cnt<=0;
  28. else
  29. cnt <= cnt + 1'b1;
  30. always@(posedge Clk or negedge Reset_n)
  31. if(!Reset_n)
  32. Send_en <= 0;
  33. else if(cnt == 1)
  34. Send_en <= 1;
  35. else if(Tx_Done)
  36. Send_en <= 0;
  37. always@(posedge Clk or negedge Reset_n)
  38. if(!Reset_n)
  39. Data <= 0;
  40. else if(Tx_Done)
  41. Data <= Data + 1'b1;//有问题
  42. endmodule

子模块文件

  1. module UART_TX(
  2. Data,
  3. Send_en,
  4. Clk,
  5. Reset_n,
  6. Uart_tx,
  7. Tx_Done,
  8. Baud_set
  9. );
  10. input [7:0] Data;
  11. input Send_en;
  12. input Clk;
  13. input Reset_n;
  14. input [2:0] Baud_set;
  15. //B_S = 0 波特率9600 计数周期 104167ns 50Mhz计数值 5208-1
  16. //B_S = 1 波特率19200计数周期 52083ns 50Mhz计数值 2604-1
  17. //B_S = 2 波特率38400计数周期 26041ns 50Mhz计数值 1302-1
  18. //B_S = 3 波特率57600计数周期 17361ns 50Mhz计数值 868-1
  19. //B_S = 4 波特率115200计数周期 8680ns 50Mhz计数值 434-1
  20. output reg Uart_tx;
  21. output reg Tx_Done;
  22. /* 若波特率115200,那么1bit需要的时间是1s/115200=8680ns,
  23. 对于50Mhz主频(20ns周期)的时钟源,计数434(9位)次为1bit 持续时间 */
  24. /* 300波特率 1s/300=3333330ns,计数3333330/20=166666次(18位) */
  25. reg [17:0] div_cnt;//用于得到波特率的基本时钟
  26. reg [17:0] bps_DR;//不同波特率对应的计数最大值
  27. wire bps_clk;
  28. assign bps_clk = (div_cnt == 1);
  29. always@(*)
  30. case(Baud_set)
  31. 0:bps_DR = 5208;
  32. 1:bps_DR = 2640;
  33. 2:bps_DR = 1302;
  34. 3:bps_DR = 868;
  35. 4:bps_DR = 434;
  36. default:bps_DR = 5208;
  37. endcase
  38. always@(posedge Clk or negedge Reset_n)//基本时钟设置(1bit)
  39. if(!Reset_n)
  40. div_cnt <=0;
  41. else if(Send_en)
  42. if(div_cnt == bps_DR-1)
  43. div_cnt <=0;
  44. else
  45. div_cnt <= div_cnt + 1'b1;
  46. else
  47. div_cnt <= 0;
  48. //1 byte数据时钟
  49. reg [3:0] bps_cnt;
  50. always@(posedge Clk or negedge Reset_n)
  51. if(!Reset_n)
  52. bps_cnt = 0;
  53. else if(Send_en) begin
  54. if(bps_clk)begin
  55. if(bps_cnt == 11)
  56. bps_cnt <= 0 ;
  57. else
  58. bps_cnt <= bps_cnt + 1'b1;
  59. end
  60. end
  61. else
  62. bps_cnt <= 0 ;
  63. always@(posedge Clk or negedge Reset_n)
  64. if(!Reset_n)begin
  65. Uart_tx <= 1'b1;
  66. end
  67. else begin
  68. case(bps_cnt)
  69. 1: Uart_tx <= 0;
  70. 2: Uart_tx <= Data[0];
  71. 3: Uart_tx <= Data[1];
  72. 4: Uart_tx <= Data[2];
  73. 5: Uart_tx <= Data[3];
  74. 6: Uart_tx <= Data[4];
  75. 7: Uart_tx <= Data[5];
  76. 8: Uart_tx <= Data[6];
  77. 9: Uart_tx <= Data[7];
  78. 10:Uart_tx <= 1;
  79. 11:Uart_tx <= 1;
  80. default:Uart_tx <= 1;
  81. endcase
  82. end
  83. always@(posedge Clk or negedge Reset_n)
  84. if(!Reset_n)
  85. Tx_Done <=0;
  86. else if((bps_clk == 1) && (bps_cnt == 10))
  87. Tx_Done <= 1;
  88. else
  89. Tx_Done <= 0;

小总结,某些标志位持续的时钟周期会对模块效果产生影响。

进一步的,如果有某个其他的模块比如说adc,每次采集好了电压值以下图形式发送出去,那么send_en-txdone机制没有集成在UART_TX模块内会导致使用不方便,因此考虑在子模块内写如send_en与txdone的运行机制,外部给出一个接口send_go,当send_go信号发生时,自动发送1byte数据。

修改子模块将send_en-txdone机制集成在子模块内。

  1. module UART_TX(
  2. Data,
  3. Send_go,
  4. Clk,
  5. Reset_n,
  6. Uart_tx,
  7. Tx_Done,
  8. Baud_set
  9. );
  10. input [7:0] Data;
  11. input Send_go;
  12. input Clk;
  13. input Reset_n;
  14. input [2:0] Baud_set;
  15. //B_S = 0 波特率9600 计数周期 104167ns 50Mhz计数值 5208-1
  16. //B_S = 1 波特率19200计数周期 52083ns 50Mhz计数值 2604-1
  17. //B_S = 2 波特率38400计数周期 26041ns 50Mhz计数值 1302-1
  18. //B_S = 3 波特率57600计数周期 17361ns 50Mhz计数值 868-1
  19. //B_S = 4 波特率115200计数周期 8680ns 50Mhz计数值 434-1
  20. output reg Uart_tx;
  21. output reg Tx_Done;
  22. /* 若波特率115200,那么1bit需要的时间是1s/115200=8680ns,
  23. 对于50Mhz主频(20ns周期)的时钟源,计数434(9位)次为1bit 持续时间 */
  24. /* 300波特率 1s/300=3333330ns,计数3333330/20=166666次(18位) */
  25. reg [17:0] div_cnt;//用于得到波特率的基本时钟
  26. reg [17:0] bps_DR;//不同波特率对应的计数最大值
  27. wire bps_clk;
  28. assign bps_clk = (div_cnt == 1);
  29. always@(*)
  30. case(Baud_set)
  31. 0:bps_DR = 5208;
  32. 1:bps_DR = 2640;
  33. 2:bps_DR = 1302;
  34. 3:bps_DR = 868;
  35. 4:bps_DR = 434;
  36. default:bps_DR = 5208;
  37. endcase
  38. reg Send_en;
  39. always@(posedge Clk or negedge Reset_n)
  40. if(!Reset_n)
  41. Send_en <= 0;
  42. else if(Send_go)
  43. Send_en <= 1;
  44. else if(Tx_Done)
  45. Send_en <= 0;
  46. always@(posedge Clk or negedge Reset_n)//基本时钟设置(1bit)
  47. if(!Reset_n)
  48. div_cnt <=0;
  49. else if(Send_en)
  50. if(div_cnt == bps_DR-1)
  51. div_cnt <=0;
  52. else
  53. div_cnt <= div_cnt + 1'b1;
  54. else
  55. div_cnt <= 0;
  56. //1 byte数据时钟
  57. reg [3:0] bps_cnt;
  58. always@(posedge Clk or negedge Reset_n)
  59. if(!Reset_n)
  60. bps_cnt = 0;
  61. else if(Send_en) begin
  62. if(bps_clk)begin
  63. if(bps_cnt == 11)
  64. bps_cnt <= 0 ;
  65. else
  66. bps_cnt <= bps_cnt + 1'b1;
  67. end
  68. end
  69. else
  70. bps_cnt <= 0 ;
  71. always@(posedge Clk or negedge Reset_n)
  72. if(!Reset_n)begin
  73. Uart_tx <= 1'b1;
  74. end
  75. else begin
  76. case(bps_cnt)
  77. 1: Uart_tx <= 0;
  78. 2: Uart_tx <= Data[0];
  79. 3: Uart_tx <= Data[1];
  80. 4: Uart_tx <= Data[2];
  81. 5: Uart_tx <= Data[3];
  82. 6: Uart_tx <= Data[4];
  83. 7: Uart_tx <= Data[5];
  84. 8: Uart_tx <= Data[6];
  85. 9: Uart_tx <= Data[7];
  86. 10:Uart_tx <= 1;
  87. 11:Uart_tx <= 1;
  88. default:Uart_tx <= 1;
  89. endcase
  90. end
  91. always@(posedge Clk or negedge Reset_n)
  92. if(!Reset_n)
  93. Tx_Done <=0;
  94. else if((bps_clk == 1) && (bps_cnt == 10))
  95. Tx_Done <= 1;
  96. else
  97. Tx_Done <= 0;
  98. endmodule

这样设计还存在一个问题,当外部信号send_go使能串口发送模块之后,在模块发送数据时,data数组发生变化(假设从第一组电压值切换到了第二组电压值),那么data的后面几个时刻可能会发送第二组电压值的数据,这样会导致发出去的8bit数据是不同组数据的组合,原因是,子模块UART_TX的data接口是直接接入发送机制的,中间没有寄存器作缓冲。因此考虑在data与发送之间加入缓冲器。修改后子模块如下

  1. module UART_TX(
  2. Data,
  3. Send_go,
  4. Clk,
  5. Reset_n,
  6. Uart_tx,
  7. Tx_Done,
  8. Baud_set
  9. );
  10. input [7:0] Data;
  11. input Send_go;
  12. input Clk;
  13. input Reset_n;
  14. input [2:0] Baud_set;
  15. //B_S = 0 波特率9600 计数周期 104167ns 50Mhz计数值 5208-1
  16. //B_S = 1 波特率19200计数周期 52083ns 50Mhz计数值 2604-1
  17. //B_S = 2 波特率38400计数周期 26041ns 50Mhz计数值 1302-1
  18. //B_S = 3 波特率57600计数周期 17361ns 50Mhz计数值 868-1
  19. //B_S = 4 波特率115200计数周期 8680ns 50Mhz计数值 434-1
  20. output reg Uart_tx;
  21. output reg Tx_Done;
  22. /* 若波特率115200,那么1bit需要的时间是1s/115200=8680ns,
  23. 对于50Mhz主频(20ns周期)的时钟源,计数434(9位)次为1bit 持续时间 */
  24. /* 300波特率 1s/300=3333330ns,计数3333330/20=166666次(18位) */
  25. reg [17:0] div_cnt;//用于得到波特率的基本时钟
  26. reg [17:0] bps_DR;//不同波特率对应的计数最大值
  27. wire bps_clk;
  28. assign bps_clk = (div_cnt == 1);
  29. always@(*)
  30. case(Baud_set)
  31. 0:bps_DR = 5208;
  32. 1:bps_DR = 2640;
  33. 2:bps_DR = 1302;
  34. 3:bps_DR = 868;
  35. 4:bps_DR = 434;
  36. default:bps_DR = 5208;
  37. endcase
  38. reg [7:0] r_Data;
  39. always@(posedge Clk )
  40. if(Send_go)
  41. r_Data <= Data;
  42. else
  43. r_Data <= r_Data;
  44. reg Send_en;
  45. always@(posedge Clk or negedge Reset_n)
  46. if(!Reset_n)
  47. Send_en <= 0;
  48. else if(Send_go)
  49. Send_en <= 1;
  50. else if(Tx_Done)
  51. Send_en <= 0;
  52. always@(posedge Clk or negedge Reset_n)//基本时钟设置(1bit)
  53. if(!Reset_n)
  54. div_cnt <=0;
  55. else if(Send_en)
  56. if(div_cnt == bps_DR-1)
  57. div_cnt <=0;
  58. else
  59. div_cnt <= div_cnt + 1'b1;
  60. else
  61. div_cnt <= 0;
  62. //1 byte数据时钟
  63. reg [3:0] bps_cnt;
  64. always@(posedge Clk or negedge Reset_n)
  65. if(!Reset_n)
  66. bps_cnt = 0;
  67. else if(Send_en) begin
  68. if(bps_clk)begin
  69. if(bps_cnt == 11)
  70. bps_cnt <= 0 ;
  71. else
  72. bps_cnt <= bps_cnt + 1'b1;
  73. end
  74. end
  75. else
  76. bps_cnt <= 0 ;
  77. always@(posedge Clk or negedge Reset_n)
  78. if(!Reset_n)begin
  79. Uart_tx <= 1'b1;
  80. end
  81. else begin
  82. case(bps_cnt)
  83. 1: Uart_tx <= 0;
  84. 2: Uart_tx <= r_Data[0];
  85. 3: Uart_tx <= r_Data[1];
  86. 4: Uart_tx <= r_Data[2];
  87. 5: Uart_tx <= r_Data[3];
  88. 6: Uart_tx <= r_Data[4];
  89. 7: Uart_tx <= r_Data[5];
  90. 8: Uart_tx <= r_Data[6];
  91. 9: Uart_tx <= r_Data[7];
  92. 10:Uart_tx <= 1;
  93. 11:Uart_tx <= 1;
  94. default:Uart_tx <= 1;
  95. endcase
  96. end
  97. always@(posedge Clk or negedge Reset_n)
  98. if(!Reset_n)
  99. Tx_Done <=0;
  100. else if((bps_clk == 1) && (bps_cnt == 10))
  101. Tx_Done <= 1;
  102. else
  103. Tx_Done <= 0;
  104. endmodule

 母模块

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/04/28 11:35:47
  7. // Design Name:
  8. // Module Name: UART_TEST
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. //设计要求,10ms发送一个数并且每次比上一次+1
  22. module UART_TEST(
  23. Clk,
  24. Reset_n,
  25. Uart_tx,
  26. LED
  27. );
  28. input Clk;
  29. input Reset_n;
  30. output Uart_tx;//子模块驱动的不要定义reg型。
  31. output reg LED;
  32. reg Send_go;
  33. reg [7:0] Data;
  34. /* reg Tx_Done; */
  35. reg[25:0] cnt1;
  36. always@(posedge Clk or negedge Reset_n)//1s/20ns
  37. if(!Reset_n)
  38. cnt1 <= 0;
  39. else if(cnt1 == 50000000-1)
  40. cnt1 <= 0;
  41. else cnt1 <= cnt1 +1'b1;
  42. always@(posedge Clk or negedge Reset_n)
  43. if(!Reset_n)
  44. LED <= 0;
  45. else if(cnt1 == 1)
  46. LED <= ~LED;
  47. else
  48. LED <= LED;
  49. UART_TX UART_byte_send(
  50. .Data(Data),
  51. .Send_go(Send_go),
  52. .Clk(Clk),
  53. .Reset_n(Reset_n),
  54. .Uart_tx(Uart_tx),
  55. .Tx_Done(Tx_Done),//子模块的输出信号可以直接调用,不需要在母模块中定义变量然后接入
  56. .Baud_set(3'd4)//子模块的入口可以接母模块的入口,也可以直接写死。
  57. );
  58. //计数器10ms 10ms/20ns=500000 19
  59. reg [18:0] cnt;
  60. always@(posedge Clk or negedge Reset_n)
  61. if(!Reset_n)
  62. cnt <= 0;
  63. else if(cnt == 500000 -1)
  64. cnt<=0;
  65. else
  66. cnt <= cnt + 1'b1;
  67. always@(posedge Clk or negedge Reset_n)
  68. if(!Reset_n)
  69. Send_go <= 0;
  70. else if(cnt == 1)
  71. Send_go <= 1;
  72. else
  73. Send_go <= 0;
  74. always@(posedge Clk or negedge Reset_n)
  75. if(!Reset_n)
  76. Data <= 0;
  77. else if(Tx_Done)
  78. Data <= Data + 1'b1;//有问题
  79. endmodule

串口发送实验2

采用状态机实现多字节发送

1.非状态机

母模块.v

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/05/03 15:47:30
  7. // Design Name:
  8. // Module Name: uart_tx_data
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module uart_tx_data(
  22. Clk,
  23. Reset_n,
  24. Data40,//40位数据输入
  25. Trans_go,//40位数据发送信号
  26. Uart_tx,
  27. Trans_down
  28. );
  29. input Clk;
  30. input Reset_n;
  31. input [39:0] Data40;
  32. input Trans_go;
  33. output Uart_tx;
  34. output reg Trans_down;
  35. reg [7:0] Data;
  36. reg Send_go;
  37. wire Tx_Done;
  38. reg [2:0] state;
  39. UART_TX UART_byte_send(
  40. .Data(Data),
  41. .Send_go(Send_go),
  42. .Clk(Clk),
  43. .Reset_n(Reset_n),
  44. .Uart_tx(Uart_tx),
  45. .Tx_Done(Tx_Done),//子模块的输出信号可以直接调用,不需要在母模块中定义变量然后接入
  46. .Baud_set(3'd4)//子模块的入口可以接母模块的入口,也可以直接写死。
  47. );
  48. always @(posedge Clk or negedge Reset_n) begin
  49. if(!Reset_n)begin
  50. state <= 0;
  51. Data <= 0;
  52. Send_go <= 0;
  53. Trans_down <= 0;
  54. end
  55. else if(state == 0)begin
  56. Trans_down <= 0;
  57. if(Trans_go)begin
  58. Data <= Data40[7:0];
  59. Send_go <= 1;
  60. state <=1;
  61. end
  62. else begin
  63. Data <= Data;
  64. Send_go <= 0;
  65. state <=0;
  66. end
  67. end
  68. else if(state == 1)begin
  69. if(Tx_Done)begin
  70. Data <= Data40[15:8];
  71. Send_go <= 1;
  72. state <= 2;
  73. end
  74. else begin
  75. Data <= Data;
  76. Send_go <= 0;
  77. state <=1;
  78. end
  79. end
  80. else if(state == 2)begin
  81. if(Tx_Done)begin
  82. Data <= Data40[23:16];
  83. Send_go <= 1;
  84. state <= 3;
  85. end
  86. else begin
  87. Data <= Data;
  88. Send_go <= 0;
  89. state <=2;
  90. end
  91. end
  92. else if(state == 3)begin
  93. if(Tx_Done)begin
  94. Data <= Data40[31:24];
  95. Send_go <= 1;
  96. state <= 4;
  97. end
  98. else begin
  99. Data <= Data;
  100. Send_go <= 0;
  101. state <=3;
  102. end
  103. end
  104. else if(state == 4)begin
  105. if(Tx_Done)begin
  106. Data <= Data40[39:32];
  107. Send_go <= 1;
  108. state <= 5;
  109. end
  110. else begin
  111. Data <= Data;
  112. Send_go <= 0;
  113. state <=4;
  114. end
  115. end
  116. else if(state == 5)begin
  117. if(Tx_Done)begin
  118. Trans_down <= 1;
  119. Send_go <= 0;
  120. state <= 0;
  121. end
  122. else begin
  123. Data <= Data;
  124. Send_go <= 0;
  125. state <=5;
  126. end
  127. end
  128. end
  129. endmodule

子模块.v

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/04/26 15:03:41
  7. // Design Name:
  8. // Module Name: UART_TX
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module UART_TX(
  22. Data,
  23. Send_go,
  24. Clk,
  25. Reset_n,
  26. Uart_tx,
  27. Tx_Done,
  28. Baud_set
  29. );
  30. input [7:0] Data;
  31. input Send_go;
  32. input Clk;
  33. input Reset_n;
  34. input [2:0] Baud_set;
  35. //B_S = 0 波特率9600 计数周期 104167ns 50Mhz计数值 5208-1
  36. //B_S = 1 波特率19200计数周期 52083ns 50Mhz计数值 2604-1
  37. //B_S = 2 波特率38400计数周期 26041ns 50Mhz计数值 1302-1
  38. //B_S = 3 波特率57600计数周期 17361ns 50Mhz计数值 868-1
  39. //B_S = 4 波特率115200计数周期 8680ns 50Mhz计数值 434-1
  40. output reg Uart_tx;
  41. output reg Tx_Done;
  42. /* 若波特率115200,那么1bit需要的时间是1s/115200=8680ns,
  43. 对于50Mhz主频(20ns周期)的时钟源,计数434(9位)次为1bit 持续时间 */
  44. /* 300波特率 1s/300=3333330ns,计数3333330/20=166666次(18位) */
  45. reg [17:0] div_cnt;//用于得到波特率的基本时钟
  46. reg [17:0] bps_DR;//不同波特率对应的计数最大值
  47. wire bps_clk;
  48. assign bps_clk = (div_cnt == 1);
  49. always@(*)
  50. case(Baud_set)
  51. 0:bps_DR = 5208;
  52. 1:bps_DR = 2640;
  53. 2:bps_DR = 1302;
  54. 3:bps_DR = 868;
  55. 4:bps_DR = 434;
  56. default:bps_DR = 5208;
  57. endcase
  58. reg [7:0] r_Data;
  59. always@(posedge Clk )
  60. if(Send_go)
  61. r_Data <= Data;
  62. else
  63. r_Data <= r_Data;
  64. reg Send_en;
  65. always@(posedge Clk or negedge Reset_n)
  66. if(!Reset_n)
  67. Send_en <= 0;
  68. else if(Send_go)
  69. Send_en <= 1;
  70. else if(Tx_Done)
  71. Send_en <= 0;
  72. always@(posedge Clk or negedge Reset_n)//基本时钟设置(1bit)
  73. if(!Reset_n)
  74. div_cnt <=0;
  75. else if(Send_en)
  76. if(div_cnt == bps_DR-1)
  77. div_cnt <=0;
  78. else
  79. div_cnt <= div_cnt + 1'b1;
  80. else
  81. div_cnt <= 0;
  82. //1 byte数据时钟
  83. reg [3:0] bps_cnt;
  84. always@(posedge Clk or negedge Reset_n)
  85. if(!Reset_n)
  86. bps_cnt = 0;
  87. else if(Send_en) begin
  88. if(bps_clk)begin
  89. if(bps_cnt == 11)
  90. bps_cnt <= 0 ;
  91. else
  92. bps_cnt <= bps_cnt + 1'b1;
  93. end
  94. end
  95. else
  96. bps_cnt <= 0 ;
  97. always@(posedge Clk or negedge Reset_n)
  98. if(!Reset_n)begin
  99. Uart_tx <= 1'b1;
  100. end
  101. else begin
  102. case(bps_cnt)
  103. 1: Uart_tx <= 0;
  104. 2: Uart_tx <= r_Data[0];
  105. 3: Uart_tx <= r_Data[1];
  106. 4: Uart_tx <= r_Data[2];
  107. 5: Uart_tx <= r_Data[3];
  108. 6: Uart_tx <= r_Data[4];
  109. 7: Uart_tx <= r_Data[5];
  110. 8: Uart_tx <= r_Data[6];
  111. 9: Uart_tx <= r_Data[7];
  112. 10:Uart_tx <= 1;
  113. 11:Uart_tx <= 1;
  114. default:Uart_tx <= 1;
  115. endcase
  116. end
  117. always@(posedge Clk or negedge Reset_n)
  118. if(!Reset_n)
  119. Tx_Done <=0;
  120. else if((bps_clk == 1) && (bps_cnt == 10))
  121. Tx_Done <= 1;
  122. else
  123. Tx_Done <= 0;
  124. endmodule

tb

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/05/03 18:31:58
  7. // Design Name:
  8. // Module Name: uart_tx_data_tb
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module uart_tx_data_tb(
  22. );
  23. reg Clk;
  24. reg Reset_n;
  25. reg [39:0] Data40;
  26. reg Trans_go;
  27. wire Uart_tx;
  28. uart_tx_data uart_tx_datainst(
  29. Clk,
  30. Reset_n,
  31. Data40,//40位数据输入
  32. Trans_go,//40位数据发送信号
  33. Uart_tx,
  34. Trans_down
  35. );
  36. initial Clk = 1;
  37. always #10 Clk = ~Clk;
  38. initial begin
  39. Reset_n = 0;
  40. Data40 = 0;
  41. Trans_go = 0;
  42. #201;
  43. Reset_n = 1;
  44. #200;
  45. Data40 = 40'h123456789a;
  46. Trans_go =1;
  47. #20;
  48. Trans_go = 0;
  49. @(posedge Trans_down);
  50. #200000;
  51. Data40 = 40'ha987654321;
  52. Trans_go =1;
  53. #20;
  54. Trans_go = 0;
  55. @(posedge Trans_down);
  56. #200000;
  57. $stop;
  58. end
  59. endmodule

2.状态机 将母模块ifelse修改为 case

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2024/05/03 18:56:20
  7. // Design Name:
  8. // Module Name: uart_tx_data2
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module uart_tx_data2(
  22. Clk,
  23. Reset_n,
  24. Data40,//40位数据输入
  25. Trans_go,//40位数据发送信号
  26. Uart_tx,
  27. Trans_down
  28. );
  29. input Clk;
  30. input Reset_n;
  31. input [39:0] Data40;
  32. input Trans_go;
  33. output Uart_tx;
  34. output reg Trans_down;
  35. reg [7:0] Data;
  36. reg Send_go;
  37. wire Tx_Done;
  38. reg [2:0] state;
  39. UART_TX UART_byte_send(
  40. .Data(Data),
  41. .Send_go(Send_go),
  42. .Clk(Clk),
  43. .Reset_n(Reset_n),
  44. .Uart_tx(Uart_tx),
  45. .Tx_Done(Tx_Done),//子模块的输出信号可以直接调用,不需要在母模块中定义变量然后接入
  46. .Baud_set(3'd4)//子模块的入口可以接母模块的入口,也可以直接写死。
  47. );
  48. always @(posedge Clk or negedge Reset_n) begin
  49. if(!Reset_n)begin
  50. state <= 0;
  51. Data <= 0;
  52. Send_go <= 0;
  53. Trans_down <= 0;
  54. end
  55. else begin
  56. case(state)
  57. 0:
  58. begin
  59. Trans_down <= 0;
  60. if(Trans_go)begin
  61. Data <= Data40[7:0];
  62. Send_go <= 1;
  63. state <=1;
  64. end
  65. else begin
  66. Data <= Data;
  67. Send_go <= 0;
  68. state <=0;
  69. end
  70. end
  71. 1:
  72. begin
  73. if(Tx_Done)begin
  74. Data <= Data40[15:8];
  75. Send_go <= 1;
  76. state <= 2;
  77. end
  78. else begin
  79. Data <= Data;
  80. Send_go <= 0;
  81. state <=1;
  82. end
  83. end
  84. 2:
  85. begin
  86. if(Tx_Done)begin
  87. Data <= Data40[23:16];
  88. Send_go <= 1;
  89. state <= 3;
  90. end
  91. else begin
  92. Data <= Data;
  93. Send_go <= 0;
  94. state <=2;
  95. end
  96. end
  97. 3:
  98. begin
  99. if(Tx_Done)begin
  100. Data <= Data40[31:24];
  101. Send_go <= 1;
  102. state <= 4;
  103. end
  104. else begin
  105. Data <= Data;
  106. Send_go <= 0;
  107. state <=3;
  108. end
  109. end
  110. 4:
  111. begin
  112. if(Tx_Done)begin
  113. Data <= Data40[39:32];
  114. Send_go <= 1;
  115. state <= 5;
  116. end
  117. else begin
  118. Data <= Data;
  119. Send_go <= 0;
  120. state <=4;
  121. end
  122. end
  123. 5:
  124. begin
  125. if(Tx_Done)begin
  126. Trans_down <= 1;
  127. Send_go <= 0;
  128. state <= 0;
  129. end
  130. else begin
  131. Data <= Data;
  132. Send_go <= 0;
  133. state <=5;
  134. end
  135. end
  136. default:
  137. begin
  138. Data <= Data;
  139. Send_go <= 0;
  140. state <=0;
  141. end
  142. endcase
  143. end
  144. end
  145. endmodule

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/780208
推荐阅读
相关标签
  

闽ICP备14008679号