集成电路技术分享

 找回密码
 我要注册

QQ登录

只需一步,快速开始

搜索
查看: 3055|回复: 8

基于ZX-2型FPGA开发板的串口示波器(五)

[复制链接]
小梅哥 发表于 2015-4-9 11:32:31 | 显示全部楼层 |阅读模式
基于ZX-2型FPGA开发板的串口示波器(五)
                                        系统仿真验证及testbench的编写
仿真验证:
以上分部分介绍了系统的各个关键模块的设计。接下来,我们来对该设计进行仿真验证。因为该实验是基于串口的,为了实现仿真验证,这里小梅哥分别编写了一个串口发送的仿真模型(Uart_Tx_Model)和一个串口接收的仿真模型(Uart_Rx_Model),两个仿真模型的设计都较为简单,但是我们却可以通过该模型模拟对我们的设计进行串口数据的发送和接收,并实时打印仿真模型发送的数据与接收到的数据。关于仿真模型的代码,这里只贴上代码,不做具体解释。(此贴回复超过100条我就专门开文讲解testbench的编写技巧)

以下为串口接收仿真模型的代码:

  1. 001        `timescale 1ns/1ps
  2. 002
  3. 003        module Uart_RX_Model(Baud_Set,uart_rx);
  4. 004               
  5. 005                input [2:0]Baud_Set;/*波特率选择信号*/
  6. 006                input uart_rx;/*仿真模型串口接收引脚*/
  7. 007               
  8. 008                reg Clk;/*仿真模型内部时钟,50M*/
  9. 009                reg Rst_n;/*仿真模型内部复位信号*/
  10. 010               
  11. 011                wire Mid_Flag_Receive;/*数据中点(采样点)标志信号*/
  12. 012               
  13. 013                reg Receive_Baud_Start;/*接收波特率生成使能信号*/
  14. 014                reg [7:0]rx_data;/*接收数据移位寄存器*/
  15. 015               
  16. 016                reg [7:0]Rx_Byte;/*最终接收结果*/
  17. 017                       
  18. 018                initial Clk = 1;
  19. 019                always#10 Clk = ~Clk;
  20. 020               
  21. 021        /*例化波特率设置模块*/       
  22. 022                baud_select baud_select_Receive(
  23. 023                        .Clk(Clk),
  24. 024                        .Rst_n(Rst_n),
  25. 025                        .Baud_Set(Baud_Set),
  26. 026                        .Baud_Start(Receive_Baud_Start),
  27. 027                        .Mid_Flag(Mid_Flag_Receive)
  28. 028                );
  29. 029               
  30. 030                initial begin
  31. 031                        Rst_n = 0;
  32. 032                        Rx_Byte = 0;
  33. 033                        rx_data = 0;
  34. 034                        #100 Rst_n = 1;
  35. 035                end
  36. 036
  37. 037        /*接收一个字节的数据*/
  38. 038                initial begin
  39. 039                forever begin
  40. 040                        @(negedge uart_rx)
  41. 041                                begin
  42. 042                                        Receive_Baud_Start = 1;
  43. 043                                        @(posedge Mid_Flag_Receive);
  44. 044                                        @(posedge Mid_Flag_Receive)rx_data[0] = uart_rx;
  45. 045                                        @(posedge Mid_Flag_Receive)rx_data[1] = uart_rx;       
  46. 046                                        @(posedge Mid_Flag_Receive)rx_data[2] = uart_rx;       
  47. 047                                        @(posedge Mid_Flag_Receive)rx_data[3] = uart_rx;
  48. 048                                        @(posedge Mid_Flag_Receive)rx_data[4] = uart_rx;       
  49. 049                                        @(posedge Mid_Flag_Receive)rx_data[5] = uart_rx;
  50. 050                                        @(posedge Mid_Flag_Receive)rx_data[6] = uart_rx;
  51. 051                                        @(posedge Mid_Flag_Receive)rx_data[7] = uart_rx;
  52. 052                                        @(posedge Mid_Flag_Receive)begin Receive_Baud_Start = 0;Rx_Byte = rx_data;end
  53. 053                                        $display("Master_receive Data = %0h",Rx_Byte);
  54. 054                                end
  55. 055                        end
  56. 056                end
  57. 057
  58. 058        endmodule
复制代码


以下为串口发送仿真模型的设计代码

  1. 001        `timescale 1ns/1ps
  2. 002
  3. 003        module Uart_Tx_Model(Baud_Set,Tx_Data,Tx_En,uart_tx,Tx_Done);
  4. 004               
  5. 005                input [2:0]Baud_Set;        /*波特率选择信号*/
  6. 006                input [7:0]Tx_Data;        /*待发送数据字节*/
  7. 007                input Tx_En;                        /*数据字节发送使能信号*/
  8. 008                output reg uart_tx;        /*仿真串口发送模型发送信号*/
  9. 009                output reg Tx_Done;        /*发送完成信号*/
  10. 010               
  11. 011                reg Clk;        /*仿真模型内部工作时钟*/
  12. 012                reg Rst_n;        /*仿真模型内部复位信号*/
  13. 013               
  14. 014                wire Bps_Clk;        /*发送波特率时钟波特率*/
  15. 015                reg Bps_En;        /*发送波特率使能信号*/
  16. 016                       
  17. 017                initial Clk = 1;
  18. 018                always#10 Clk = ~Clk;
  19. 019
  20. 020        /*----例化发送波特率时钟生成模块-----*/       
  21. 021                TxModel_Bps_Gen TxModel_Bps_Gen_send(
  22. 022                        .Clk(Clk),
  23. 023                        .Rst_n(Rst_n),
  24. 024                        .Baud_Set(Baud_Set),
  25. 025                        .Tx_Done(Tx_Done),
  26. 026                        .Bps_Clk(Bps_Clk),
  27. 027                        .Byte_En(Bps_En)
  28. 028                );
  29. 029                       
  30. 030                initial begin
  31. 031                        Tx_Done = 0;
  32. 032                        uart_tx = 1;
  33. 033                        Rst_n = 0;
  34. 034                        Bps_En = 0;
  35. 035                        #100;
  36. 036                        Rst_n = 1;
  37. 037                        forever@(posedge Tx_En)/*每来一个发送使能信号即执行一次发送过程*/
  38. 038                                Uart_Send(Tx_Data);       
  39. 039                end
  40. 040
  41. 041        /*执行一次字节数据的发送*/       
  42. 042                task Uart_Send;
  43. 043                        input [7:0]Data;
  44. 044                        begin
  45. 045                                Bps_En = 1;
  46. 046                                Tx_Done = 0;
  47. 047                                $display("Uart_Send Data = %0h",Data);/*打印发送的数据*/
  48. 048                                @(posedge Bps_Clk) #0.1 uart_tx = 0;
  49. 049                                @(posedge Bps_Clk) #0.1 uart_tx = Data[0];
  50. 050                                @(posedge Bps_Clk) #0.1 uart_tx = Data[1];
  51. 051                                @(posedge Bps_Clk) #0.1 uart_tx = Data[2];
  52. 052                                @(posedge Bps_Clk) #0.1 uart_tx = Data[3];
  53. 053                                @(posedge Bps_Clk) #0.1 uart_tx = Data[4];
  54. 054                                @(posedge Bps_Clk) #0.1 uart_tx = Data[5];
  55. 055                                @(posedge Bps_Clk) #0.1 uart_tx = Data[6];
  56. 056                                @(posedge Bps_Clk) #0.1 uart_tx = Data[7];
  57. 057                                @(posedge Bps_Clk) #0.1 uart_tx = 1;
  58. 058                                @(posedge Bps_Clk) #0.1 ;
  59. 059                                Tx_Done = 1;
  60. 060                                Bps_En = 0;
  61. 061                                #20 Tx_Done = 0;
  62. 062                        end
  63. 063                endtask
  64. 064               
  65. 065        endmodule
复制代码

以下为仿真顶层模块的设计

  1. 001        `timescale 1ns/1ns
  2. 002        `include "../rtl/header.v"
  3. 003        module uart_scope_tb;
  4. 004                localparam         KEY_WIDTH = 3;
  5. 005               
  6. 006                reg Clk;
  7. 007                reg Rst_n;
  8. 008                reg [KEY_WIDTH - 1:0]Key_in;
  9. 009               
  10. 010                reg ADC_Din;
  11. 011                wire ADC_Clk;
  12. 012                wire ADC_Cs_n;
  13. 013               
  14. 014        /*波特率设置总线,此处默认为9600bps,仿真不做波特率修改测试*/       
  15. 015                wire [2:0]Baud_Set;
  16. 016                reg [7:0]Tx_Data;/*串口发送仿真模型待发送数据字节*/
  17. 017                reg Tx_En;        /*串口发送仿真模型发送使能信号*/
  18. 018                wire Rs232_MTSR;        /*串口“主机(PC)发送-从机(FPGA)接收”信号*/
  19. 019                wire Rs232_MRST;        /*串口“主机(PC)接收-从机(FPGA)发送”信号*/
  20. 020                wire Tx_Done;        /*串口字节发送完成信号*/
  21. 021               
  22. 022                assign Baud_Set = 3'd0;/*设置波特率为固定的9600bps*/
  23. 023               
  24. 024                localparam
  25. 025                        Header = 8'hAA,        /*帧头*/
  26. 026                        Length = 8'd3,                /*帧长*/
  27. 027                        Tail   = 8'h88;        /*帧尾*/
  28. 028
  29. 029        /*------例化串口示波器顶层模块------*/
  30. 030                uart_scope uart_scope(
  31. 031                        .Clk(Clk),
  32. 032                        .Rst_n(Rst_n),
  33. 033                        .Rs232_Rx(Rs232_MTSR),
  34. 034                        .Rs232_Tx(Rs232_MRST),
  35. 035                        .Key_in(Key_in),
  36. 036                        .ADC_Din(ADC_Din),
  37. 037                        .ADC_Clk(ADC_Clk),
  38. 038                        .ADC_Cs_n(ADC_Cs_n)
  39. 039                );
  40. 040               
  41. 041        /*------例化串口发送仿真模型------*/
  42. 042                Uart_Tx_Model Uart_Tx_Model(
  43. 043                        .Baud_Set(Baud_Set),
  44. 044                        .Tx_Data(Tx_Data),
  45. 045                        .Tx_En(Tx_En),
  46. 046                        .uart_tx(Rs232_MTSR),
  47. 047                        .Tx_Done(Tx_Done)
  48. 048                );
  49. 049               
  50. 050        /*------例化串口接收仿真模型------*/
  51. 051        //该模型接收FPGA发送出来的数据并打印在modelsim的transcript窗口中       
  52. 052                Uart_RX_Model Uart_RX_Model(
  53. 053                        .Baud_Set(Baud_Set),
  54. 054                        .uart_rx(Rs232_MRST)
  55. 055                );
  56. 056
  57. 057        /*-------生成50M时钟信号--------*/       
  58. 058                initial Clk = 0;
  59. 059                always #10 Clk = ~Clk;
  60. 060
  61. 061        /*-------生成ADC_Din数据-------*/       
  62. 062        /*此处不对ADC的采样结果多做计较,只要求保
  63. 063          证ADC_Din上有数据即可,有兴趣者可自己编写仿真模型*/
  64. 064                initial ADC_Din = 1;
  65. 065                always #1315 ADC_Din = ~ADC_Din;
  66. 066               
  67. 067                initial begin
  68. 068                        Rst_n = 1'b0;
  69. 069                        Tx_En = 1'b0;
  70. 070                        Tx_Data = 8'd0;
  71. 071                        Key_in = 4'b1111;
  72. 072                        #200;
  73. 073                        Rst_n = 1'b1;        /*释放复位信号,系统即进入正常工作状态*/
  74. 074                        #1000;
  75. 075                        En_DDS_Run;        /*使能DDS信号发生器生成信号数据*/
  76. 076                        #10000;
  77. 077                        En_S_DDS;        /*使能采样ADC数据*/
  78. 078                        En_S_ADC;        /*使能采样DDS数据*/
  79. 079                        #10000;
  80. 080                        En_UART_Send;/*使能串口发送,此时串口猎人软件上将会开始持续接收到数据*/       
  81. 081                end
  82. 082               
  83. 083                initial begin
  84. 084                #200_000_000;press_key(0);
  85. 085                #200_000_000;press_key(1);
  86. 086                #200_000_000;
  87. 087                $stop;
  88. 088                end
  89. 089               
  90. 090               
  91. 091
  92. 092        /*---发送命令帧数据任务-----*/       
  93. 093                task Send_CMD;
  94. 094                        input [7:0]DATAA,DATAB,DATAC;/*用户数据(地址、数据高字节,数据低字节)*/
  95. 095                        begin
  96. 096                                Tx_Data = Header;/*需发送数据为帧头*/
  97. 097                                Tx_En = 1;        /*启动发送*/
  98. 098                                #20 Tx_En = 0;        /*一个时钟周期后,清零发送启动信号*/
  99. 099                                @(posedge Tx_Done)/*等待发送完成信号*/
  100. 100                                #1000;
  101. 101                               
  102. 102                                Tx_Data = Length;/*需发送数据为帧长,此处帧长只是数据内容的长度*/
  103. 103                                Tx_En = 1;        /*启动发送*/
  104. 104                                #20 Tx_En = 0;        /*一个时钟周期后,清零发送启动信号*/
  105. 105                                @(posedge Tx_Done)/*等待发送完成信号*/
  106. 106                                #1000;
  107. 107                               
  108. 108                                Tx_Data = DATAA;/*需发送数据第一个字节,此数据代表外设寄存器的地址*/
  109. 109                                Tx_En = 1;        /*启动发送*/
  110. 110                                #20 Tx_En = 0;        /*一个时钟周期后,清零发送启动信号*/
  111. 111                                @(posedge Tx_Done)/*等待发送完成信号*/
  112. 112                                #1000;
  113. 113                               
  114. 114                                Tx_Data = DATAB;/*需发送数据第二个字节,此数据代表写入外设寄存器的内容高8位*/
  115. 115                                Tx_En = 1;        /*启动发送*/
  116. 116                                #20 Tx_En = 0;        /*一个时钟周期后,清零发送启动信号*/
  117. 117                                @(posedge Tx_Done)/*等待发送完成信号*/
  118. 118                                #1000;
  119. 119                               
  120. 120                                Tx_Data = DATAC;/*需发送数据第三个字节,此数据代表写入外设寄存器的内容低8位*/
  121. 121                                Tx_En = 1;        /*启动发送*/
  122. 122                                #20 Tx_En = 0;        /*一个时钟周期后,清零发送启动信号*/
  123. 123                                @(posedge Tx_Done)/*等待发送完成信号*/
  124. 124                                #1000;
  125. 125                               
  126. 126                                Tx_Data = Tail;/*需发送数据为帧尾*/
  127. 127                                Tx_En = 1;        /*启动发送*/
  128. 128                                #20 Tx_En = 0;        /*一个时钟周期后,清零发送启动信号*/
  129. 129                                @(posedge Tx_Done)/*等待发送完成信号*/
  130. 130                                #1000;
  131. 131                                #10000;       
  132. 132                        end
  133. 133                endtask       
  134. 134               
  135. 135                task En_DDS_Run;/*使能DDS生成数据*/
  136. 136                        begin
  137. 137                                Send_CMD(`DDS_En, 8'h00, 8'h01);
  138. 138                                $display("En DDS Run");
  139. 139                        end
  140. 140                endtask
  141. 141               
  142. 142                task Stop_DDS_Run;/*停止DDS生成数据*/
  143. 143                        begin
  144. 144                                Send_CMD(`DDS_En, 8'h00, 8'h00);
  145. 145                                $display("Stop DDS Run");
  146. 146                        end
  147. 147                endtask
  148. 148               
  149. 149                task En_S_DDS;/*使能采样DDS数据*/
  150. 150                        begin
  151. 151                                Send_CMD(`DDS_Sample_En, 8'h00, 8'h01);
  152. 152                                $display("En Sample DDS data");
  153. 153                        end
  154. 154                endtask
  155. 155               
  156. 156                task Stop_S_DDS;/*停止采样DDS数据*/
  157. 157                        begin
  158. 158                                Send_CMD(`DDS_Sample_En, 8'h00, 8'h00);
  159. 159                                $display("Stop Sample DDS data");
  160. 160                        end
  161. 161                endtask
  162. 162               
  163. 163                task En_UART_Send;/*使能串口发送*/
  164. 164                        begin
  165. 165                                Send_CMD(`UART_En_Tx, 8'h00, 8'h01);
  166. 166                                $display("En UART Send");
  167. 167                        end
  168. 168                endtask
  169. 169               
  170. 170                task Stop_UART_Send;/*停止串口发送*/
  171. 171                        begin
  172. 172                                Send_CMD(`UART_En_Tx, 8'h00, 8'h00);
  173. 173                                $display("Stop UART Send");
  174. 174                        end
  175. 175                endtask
  176. 176               
  177. 177                        task En_S_ADC;/*使能采集ADC数据*/
  178. 178                        begin
  179. 179                                Send_CMD(`ADC_Sample_En, 8'h00, 8'h01);
  180. 180                                $display("En Sample ADC data");
  181. 181                        end
  182. 182                endtask
  183. 183               
  184. 184                task Stop_S_ADC;/*停止采集ADC数据*/
  185. 185                        begin
  186. 186                                Send_CMD(`ADC_Sample_En, 8'h00, 8'h00);
  187. 187                                $display("Stop Sample ADC data");
  188. 188                        end
  189. 189                endtask
  190. 190
  191. 191                task Set_ADC_Sample_Speed;/*设置ADC采样率*/
  192. 192                        input[25:0] Fs;/*采样率实际频率*/
  193. 193                        reg [31:0] S_cnt_top;/*分频计数器计数最大值*/
  194. 194                        begin
  195. 195                        /*由采样实际频率值换算出采样分频计数器计数最大值*/
  196. 196                                S_cnt_top = 50000000/Fs - 1;
  197. 197                        /*写采样分频计数器计数最大值低16位*/
  198. 198                                Send_CMD(`ADC_S_Cnt_Max_L,S_cnt_top[15:8],S_cnt_top[7:0]);
  199. 199                        /*写采样分频计数器计数最大值高16位*/
  200. 200                                Send_CMD(`ADC_S_Cnt_Max_H,S_cnt_top[31:24],S_cnt_top[23:16]);
  201. 201                                $display("Set ADC Sample Speed as  = %0d" ,Fs);
  202. 202                        end
  203. 203                endtask
  204. 204               
  205. 205                task Set_DDS_Sample_Speed;/*设置DDS数据的采样率*/
  206. 206                        input[25:0] Fs;/*采样率实际频率*/
  207. 207                        reg [31:0] S_cnt_top;/*分频计数器计数最大值*/
  208. 208                        begin
  209. 209                        /*由采样实际频率值换算出采样分频计数器计数最大值*/
  210. 210                                S_cnt_top = 50000000/Fs - 1;
  211. 211                        /*写采样分频计数器计数最大值低16位*/       
  212. 212                                Send_CMD(`DDS_S_Cnt_Max_L,S_cnt_top[15:8],S_cnt_top[7:0]);
  213. 213                        /*写采样分频计数器计数最大值高16位*/
  214. 214                                Send_CMD(`DDS_S_Cnt_Max_H,S_cnt_top[31:24],S_cnt_top[23:16]);
  215. 215                                $display("Set DDS Sample Speed as  = %0d" ,Fs);
  216. 216                        end
  217. 217                endtask
  218. 218               
  219. 219                task Set_DDS_Fout_Speed;/*设置DDS输出信号频率*/
  220. 220                        input[25:0] Fs;/*输出信号实际频率*/
  221. 221                        reg [31:0] r_fword;/*DDS频率控制字*/
  222. 222                        begin
  223. 223                        /*由实际要求输出频率数据换算出频率控制字*/
  224. 224                                r_fword = Fs*65536*65536/50000000;
  225. 225                                Send_CMD(`DDS_Fword_L,r_fword[15:8],r_fword[7:0]);
  226. 226                                Send_CMD(`DDS_Fword_H,r_fword[31:24],r_fword[23:16]);
  227. 227                                $display("Set DDS Fout as = %0d" ,Fs);
  228. 228                        end
  229. 229                endtask
  230. 230               
  231. 231               
  232. 232                task press_key;
  233. 233                        input [KEY_WIDTH/2:0]Key;
  234. 234                        reg [15:0]myrand;
  235. 235                        begin
  236. 236                                Key_in = {KEY_WIDTH{1'b1}};
  237. 237                                /*按下抖动*/
  238. 238                                repeat(20)begin
  239. 239                                        myrand = {$random} % 65536;
  240. 240                                        #myrand Key_in[Key] = ~Key_in[Key];
  241. 241                                end
  242. 242                                Key_in[Key] = 1'b0;
  243. 243                                                       
  244. 244                                #22000000;/*稳定期*/
  245. 245                               
  246. 246                                /*释放抖动*/
  247. 247                                repeat(20)begin
  248. 248                                        myrand = {$random} % 65536;
  249. 249                                        #myrand Key_in[Key] = ~Key_in[Key];
  250. 250                                end
  251. 251                                Key_in[Key] = 1'b1;
  252. 252                                #22000000;/*稳定期*/
  253. 253                        end
  254. 254                endtask
  255. 255                       
  256. 256        endmodule
复制代码

下图为系统仿真架构图:

这里,在我们提供的工程中,已经设置好了Nativelink,用户只需要在Quartus II中点击tools—run rtl simulation tool—rtl simulation即可自动调用modelsim-altera并执行仿真,因为这里完全模拟真实时序进行仿真,因此运行完整个仿真大约需要5—10分钟。
仿真完成后,结果如图所示:

其中,Rx_Byte为串口接收仿真模型接收到的数据,这里以波形的方式展示。ADC_Data为ADC采样结果,DDS_Data为DDS输出的数据最下方为按键标志和按键结果,当按下按键1时,数据通道切换为ADC的采样结果,当按下按键2时,数据通道切换为DDS的输出数据。
(如果用户在进行仿真的过程中发现仿真无法运行,在modelsim中提示错误的话,请删除simulation—>modelsim文件夹下除wave.do和mydo.do文件外的其他所有文件,然后在quartus 中重新启动仿真)

小梅哥
2015年4月8日 于至芯科技

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?我要注册

x
 楼主| 小梅哥 发表于 2015-4-9 11:45:09 | 显示全部楼层
基于ZX-2型FPGA开发板的串口示波器(一)
http://www.fpgaw.com/forum.php?m ... 0&fromuid=28191
(出处: fpga论坛|fpga设计论坛)


基于ZX-2型FPGA开发板的串口示波器(二)
http://www.fpgaw.com/forum.php?m ... 1&fromuid=28191
(出处: fpga论坛|fpga设计论坛)


基于ZX-2型FPGA开发板的串口示波器(三)
http://www.fpgaw.com/forum.php?m ... 2&fromuid=28191
(出处: fpga论坛|fpga设计论坛)


基于ZX-2型FPGA开发板的串口示波器(四)
http://www.fpgaw.com/forum.php?m ... 3&fromuid=28191
(出处: fpga论坛|fpga设计论坛)


基于ZX-2型FPGA开发板的串口示波器(五)
http://www.fpgaw.com/forum.php?m ... 4&fromuid=28191
(出处: fpga论坛|fpga设计论坛)


基于ZX-2型FPGA开发板的串口示波器(六)
http://www.fpgaw.com/forum.php?m ... 5&fromuid=28191
(出处: fpga论坛|fpga设计论坛)
 楼主| 小梅哥 发表于 2015-4-9 11:47:06 | 显示全部楼层
需要工程的请关注至芯官方微信号

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?我要注册

x
寻你灯火阑珊处 发表于 2015-4-12 10:04:02 | 显示全部楼层
工程是次要的,技术文档体现的方法和思想是关键,从代码也可以学到很好的风格,怒刷赞
telewuhun 发表于 2015-4-17 16:57:01 | 显示全部楼层
太牛逼了,值得学习啊~~
Sunlife 发表于 2015-7-29 09:34:31 | 显示全部楼层
                看了你的帖子 瞬间爱上你了怎么办
同人于郊 发表于 2015-8-1 16:37:04 | 显示全部楼层
感谢小梅哥的无私分享,小弟邮箱     759896891@qq.com    麻烦了
您需要登录后才可以回帖 登录 | 我要注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|小黑屋|手机版|Archiver|集成电路技术分享 ( 京ICP备20003123号-1 )

GMT+8, 2024-5-4 14:54 , Processed in 0.079429 second(s), 20 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表