【Verilog HDL 训练】第 07 天(串并转换)

举报
李锐博恩 发表于 2021/07/15 03:49:24 2021/07/15
【摘要】 串并转换 1. 复习verilog语法 【选做题】 - 文件操作fopen fdisplay fwrite fclose - 生成随机数 random - 初始化 readmemh readmemb - finish stop 这几个我真没用过,先给一个优秀的链接:FPGA篇(四)Verilog系统函数介绍($display,$fopen,$fscanf,$fw...

串并转换

1. 复习verilog语法

【选做题】

- 文件操作fopen fdisplay fwrite fclose

- 生成随机数 random

- 初始化 readmemh readmemb - finish stop


这几个我真没用过,先给一个优秀的链接:FPGA篇(四)Verilog系统函数介绍($display,$fopen,$fscanf,$fwrite($fdisplay),$fclose,$random,$stop)

再借鉴一篇:

本题借鉴别人的:verilog-day7.md

  • 文件操作fopen fdisplay fwrite fclose

     integer fileunder;
     fileunder = $fopen("FileName");//打开文件,返回一个整型,会清空文件
     $fdisplay(fileunder,"%d",mumber);//写入操作,写完换行
     $fwrite(fileunder,"%d",mumber);//写入操作,写完不换行
     $fclose(fileunder);//关闭文件
  • 生成随机数random

    每次调用$random任务时,它返回一个32位带符号的随机整数。将$random放入{}内,可以得到非负整数。$random(seed)中的seed是一个整数,用于指出随机数的取值范围。

     rand = $random % 60;//给出了一个范围在-59到59之间的随机数。
     rand = {$random} % 60;//通过位并接操作产生一个值在0到59之间的数。
     rand = min+{$random} % (max-min+1);//产生一个在min, max之间随机数的例子。
  • 初始化 readmemh readmemb

    把文本文件的数据读到存储器阵列中,以对存储器阵列完成初始化。

     $readmemb("<数据文件名>",<存储器名>);
     $readmemb("<数据文件名>",<存储器名>,<起始地址>);
     $readmemb("<数据文件名>",<存储器名>,<起始地址>,<终止地址>); $readmemh("<数据文件名>",<存储器名>);
     $readmemh("<数据文件名>",<存储器名>,<起始地址>);
     $readmemh("<数据文件名>",<存储器名>,<起始地址>,<终止地址>); $readmemb中要求数据必须为二进制,$readmemh要求数据必须为十六进制
  • finish stop

    $stop:用于在仿真时,暂停仿真。运行到$stop的时候,仿真会暂停;此时可以在命令行输入run继续运行仿真。

    $finish:仿真停止。运行到$finish的时候,仿真停止退出,此时不可以再继续运行


2. 用verilog实现串并变换。

input [3:0] data_in; output [3:0] data_out; input [1:0] mode; input clk; input rst_n;

mode 0 :串行输入data_in[0],并行输出data_out[3:0]

mode 1 :并行输入data_in[3:0],串行输出data_out[0]

mode 2 :并行输入data_in[3:0],并行输出data_out[3:0],延迟1个时钟周期

mode 3 :并行输入data_in[3:0],并行反序输出data_out[3:0],延迟1个时钟周期并且交换bit顺序

data_out[3]=data_in[0];

data_out[2]=data_in[1]

data_out[1]=data_in[2]

data_out[0]=data_in[3]

附加要求【选做】 将输入输出的位宽做成参数化


先写一个四选一的多路选择器,供顶层文件使用:


  
  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2019/05/03 13:46:33
  7. // Design Name:
  8. // Module Name: mux2b
  9. //
  10. module mux2b #(parameter N = 4) (
  11. input [1 : 0]mode,
  12. input [N-1 : 0] a,
  13. input [N-1 : 0] b,
  14. input [N-1 : 0] c,
  15. input [N-1 : 0] d,
  16. output reg [N-1 : 0] data_out
  17. );
  18. localparam M0 = 2'b00, M1 = 2'b01, M2 = 2'b10, M3 = 2'b11;
  19. always @ (*) begin
  20. case(mode)
  21. M0: data_out = a;
  22. M1: data_out = b;
  23. M2: data_out = c;
  24. M3: data_out = d;
  25. default: ;
  26. endcase
  27. end
  28. endmodule

顶层文件:


  
  1. `timescale 1ns / 1ps
  2. //
  3. // Create Date: 2019/04/30 13:00:03
  4. // Design Name:
  5. // Module Name: Serial2Parallel
  6. // Revision 0.01 - File Created
  7. // Additional Comments:
  8. // 2. 用verilog实现串并变换。
  9. /* input [3:0] data_in; output [3:0] data_out; input [1:0] mode; input clk; input rst_n;
  10. mode 0 :串行输入data_in[0],并行输出data_out[3:0]
  11. mode 1 :并行输入data_in[3:0],串行输出data_out[0]
  12. mode 2 :并行输入data_in[3:0],并行输出data_out[3:0],延迟1个时钟周期
  13. mode 3 :并行输入data_in[3:0],并行反序输出data_out[3:0],延迟1个时钟周期并且交换bit顺序
  14. data_out[3]=data_in[0];
  15. data_out[2]=data_in[1]
  16. data_out[1]=data_in[2]
  17. data_out[0]=data_in[3]
  18. 附加要求【选做】 将输入输出的位宽做成参数化 */
  19. //
  20. module Serial2Parallel #(parameter N = 4) (
  21. input [N-1:0] data_in,
  22. input [1:0] mode,
  23. input clk,
  24. input rst_n,
  25. output [N-1:0] data_out
  26. );
  27. localparam
  28. M0 = 2'b00, //mode 0 :串行输入data_in[0],并行输出data_out[3:0]
  29. M1 = 2'b01, //mode 1 :并行输入data_in[3:0],串行输出data_out[0]
  30. M2 = 2'b10, //mode 2 :并行输入data_in[3:0],并行输出data_out[3:0],延迟1个时钟周期
  31. M3 = 2'b11; //mode 3 :并行输入data_in[3:0],并行反序输出data_out[3:0],延迟1个时钟周期并且交换bit顺序
  32. reg [N-1:0] data_out_mid0, data_out_mid1,data_out_mid2,data_out_mid3;
  33. integer i;
  34. //----------------------------------------------------------------
  35. //en0上升沿检测
  36. reg en0;
  37. reg en0_r0,en0_r1;
  38. always @ (posedge clk or negedge rst_n) begin
  39. if(!rst_n) begin
  40. en0 <= 0;
  41. en0_r0 <= 0;
  42. en0_r1 <= 0;
  43. end
  44. else begin
  45. en0_r0 <= en0;
  46. en0_r1 <= en0_r0;
  47. end
  48. end
  49. wire pos0_en,pos0_en1;
  50. assign pos0_en = ~en0_r0 & en0;
  51. assign pos0_en1 = ~en0_r1 & en0_r0;
  52. //en1上升沿检测
  53. reg en1;
  54. reg en1_r0,en1_r1;
  55. always @ (posedge clk or negedge rst_n) begin
  56. if(!rst_n) begin
  57. en1 <= 0;
  58. en1_r0 <= 0;
  59. en1_r1 <= 0;
  60. end
  61. else begin
  62. en1_r0 <= en1;
  63. en1_r1 <= en1_r0;
  64. end
  65. end
  66. wire pos1_en,pos1_en1;
  67. assign pos1_en = ~en1_r0 & en1;
  68. assign pos1_en1 = ~en1_r1 & en1_r0;
  69. //en2上升沿检测
  70. reg en2;
  71. reg en2_r0,en2_r1;
  72. always @ (posedge clk or negedge rst_n) begin
  73. if(!rst_n) begin
  74. en2 <= 0;
  75. en2_r0 <= 0;
  76. en2_r1 <= 0;
  77. end
  78. else begin
  79. en2_r0 <= en2;
  80. en2_r1 <= en2_r0;
  81. end
  82. end
  83. wire pos2_en,pos2_en1;
  84. assign pos2_en = ~en2_r0 & en2;
  85. assign pos2_en1 = ~en2_r1 & en2_r0;
  86. //-----------------------------------------------------------------------
  87. always@(posedge clk or negedge rst_n) begin
  88. if(!rst_n) begin
  89. data_out_mid0 <= 0;
  90. data_out_mid1 <= 0;
  91. data_out_mid2 <= 0;
  92. data_out_mid3 <= 0;
  93. end
  94. else begin
  95. case(mode)
  96. M0: begin
  97. data_out_mid0 <= {data_out_mid0[N-2:0],data_in[0]};//每次输入一位data_in[0]
  98. end
  99. M1: begin
  100. if(pos0_en) begin
  101. data_out_mid1 <= data_in;
  102. end
  103. else if(pos0_en1)begin
  104. data_out_mid1 <= data_out_mid1;
  105. end
  106. else begin
  107. data_out_mid1 <= {data_out_mid1[0],data_out_mid1[N-1:1]};
  108. end
  109. end
  110. M2: begin
  111. if(pos1_en) begin
  112. data_out_mid2 <= data_in;
  113. end
  114. else if(pos1_en1)begin
  115. data_out_mid2 <= data_out_mid2;
  116. end
  117. end
  118. M3: begin
  119. if(pos2_en) begin
  120. data_out_mid3 <= data_in;
  121. end
  122. else if(pos2_en1)begin
  123. //genvar i;
  124. //generate
  125. for(i = 0; i < N; i = i + 1) begin
  126. data_out_mid3[i] <= data_out_mid3[N-1-i];
  127. end
  128. //endgenerate
  129. end
  130. end
  131. default: ;
  132. endcase
  133. end
  134. end
  135. always @ (*) begin
  136. case(mode)
  137. M0:;
  138. M1: en0 = 1;
  139. M2: en1 = 1;
  140. M3: en2 = 1;
  141. default: ;
  142. endcase
  143. end
  144. mux2b #(.N(4)) u_data_out(
  145. .mode(mode),
  146. .a(data_out_mid0), //mode0
  147. .b(data_out_mid1), //mode1
  148. .c(data_out_mid2), //mode2
  149. .d(data_out_mid3), //mode3
  150. .data_out(data_out)
  151. );
  152. endmodule

给出测试文件:


  
  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2019/04/30 21:17:46
  7. // Design Name:
  8. // Module Name: Serial2Parallel_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 Serial2Parallel_tb(
  22. );
  23. reg clk, rst_n;
  24. reg [3:0] data_in;
  25. reg [1:0] mode;
  26. wire data_out;
  27. localparam
  28. M0 = 2'b00, //mode 0 :串行输入data_in[0],并行输出data_out[3:0]
  29. M1 = 2'b01, //mode 1 :并行输入data_in[3:0],串行输出data_out[0]
  30. M2 = 2'b10, //mode 2 :并行输入data_in[3:0],并行输出data_out[3:0],延迟1个时钟周期
  31. M3 = 2'b11; //mode 3 :并行输入data_in[3:0],并行反序输出data_out[3:0],延迟1个时钟周期并且交换bit顺序
  32. initial clk = 0;
  33. always begin
  34. #1 clk = ~clk;
  35. end
  36. initial begin
  37. rst_n = 0;
  38. #3
  39. rst_n = 1;
  40. mode = M0;
  41. data_in = 4'b0001;
  42. #2
  43. data_in = 4'b0000;
  44. #2
  45. data_in = 4'b0001;
  46. #2
  47. data_in = 4'b0000;
  48. #2
  49. data_in = 4'b0001;
  50. #5
  51. mode = M1;
  52. data_in = 4'b0100;
  53. #12
  54. mode = M2;
  55. data_in = 4'b0010;
  56. #6
  57. mode = M3;
  58. data_in = 4'b1010;
  59. end
  60. Serial2Parallel #(.N(4)) u0(
  61. .clk(clk),
  62. .rst_n(rst_n),
  63. .mode(mode),
  64. .data_in(data_in),
  65. .data_out(data_out)
  66. );
  67. endmodule

下面给出行为仿真波形图:

模式0:

由于输入是data_in[0],所有data_in[0]单独显示也许会更好:

由仿真图可见,我们的串行输入为1010111,而输出为0001,0010,0101,1010,0101,1011,0111,符合我们的并行输出。

模式0的串行输出正确。

模式1:

模式一为并行输入,串行输出,输出为data_out[0],就是上图中的[0],模式1从16ns处开始,在17ns时刻(时钟上升沿)给数据,缓冲一个周期,到19ns开始输出,输出为0010;因为输入从低到高为0010,故输出4个即可,其他输出均不是我们要的。

模式2:

模式2是并行输入,并行输出:

模式2从28ns处开始,在29ns处(时钟有效沿)给数据31ns处输出数据data_out为0010;

模式3:

模式3为并行输入,并行输出,但是反向输出,例如输入为data_in = 4'b1010,则输出data_out=4'b0101;

模式3从34ns开始,35ns给数据从低到高为0101,延迟一拍,输出为1010,满足要求。

总结:代码需要多些,多学习一些技巧,这次写的这个代码,我自己是很不满意的,因为有很多缺陷,并不能完美的实现所要的功能(例如,输出延迟一拍,那一拍之前的输出是什么?是不是应该不输出,设为高阻态),但碍于见识少等,这种困惑并不能在代码中体现。


3. 记录一下第2题中用到的工具,包括工具版本,操作步骤或命令选项,遇到的错误,提示信息等。比较一下,与昨天的记录有何相同,有何不同。

Vivado 2018

这次的代码设计用了不少时间,因为加入了mode,模式之间的切换是一个需要考虑的难点。第二道题,确实也让我学到了一些东西,这都可以成为经验,我一开始的设计,最后的data_out输出并没有使用多路选择器来实现,而是如下:

assign data_out = data_out_mid0;

assign data_out = data_out_mid1;

我现在看起来确实可笑,但是当时并没有想到,我在相关群里咨询了大佬们,大佬们一眼就看出来了问题,确实感谢它们。

上述写法,data_out和这么多变量相连,到底输出取谁呢?想想都知道走火入魔了。

但最终的整体程序,还是个人最初的思想,加以修改。

这肯定不是最好的写法,还是要想别人学习。多见识见识电路实现的技巧。

最后一个简单的问题就是,我调试代码中犯的一个小错误,就是在多路选择器的代码中,忘了给mode位数声明,导致后面的功能出现问题,后来通过仿真图及时发现并得以改正。

 

 

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/89707184

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。