小梅哥 发表于 2015-4-9 11:32:31

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

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

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

001        `timescale 1ns/1ps
002
003        module Uart_RX_Model(Baud_Set,uart_rx);
004               
005                input Baud_Set;/*波特率选择信号*/
006                input uart_rx;/*仿真模型串口接收引脚*/
007               
008                reg Clk;/*仿真模型内部时钟,50M*/
009                reg Rst_n;/*仿真模型内部复位信号*/
010               
011                wire Mid_Flag_Receive;/*数据中点(采样点)标志信号*/
012               
013                reg Receive_Baud_Start;/*接收波特率生成使能信号*/
014                reg rx_data;/*接收数据移位寄存器*/
015               
016                reg Rx_Byte;/*最终接收结果*/
017                       
018                initial Clk = 1;
019                always#10 Clk = ~Clk;
020               
021        /*例化波特率设置模块*/       
022                baud_select baud_select_Receive(
023                        .Clk(Clk),
024                        .Rst_n(Rst_n),
025                        .Baud_Set(Baud_Set),
026                        .Baud_Start(Receive_Baud_Start),
027                        .Mid_Flag(Mid_Flag_Receive)
028                );
029               
030                initial begin
031                        Rst_n = 0;
032                        Rx_Byte = 0;
033                        rx_data = 0;
034                        #100 Rst_n = 1;
035                end
036
037        /*接收一个字节的数据*/
038                initial begin
039                forever begin
040                        @(negedge uart_rx)
041                                begin
042                                        Receive_Baud_Start = 1;
043                                        @(posedge Mid_Flag_Receive);
044                                        @(posedge Mid_Flag_Receive)rx_data = uart_rx;
045                                        @(posedge Mid_Flag_Receive)rx_data = uart_rx;       
046                                        @(posedge Mid_Flag_Receive)rx_data = uart_rx;       
047                                        @(posedge Mid_Flag_Receive)rx_data = uart_rx;
048                                        @(posedge Mid_Flag_Receive)rx_data = uart_rx;       
049                                        @(posedge Mid_Flag_Receive)rx_data = uart_rx;
050                                        @(posedge Mid_Flag_Receive)rx_data = uart_rx;
051                                        @(posedge Mid_Flag_Receive)rx_data = uart_rx;
052                                        @(posedge Mid_Flag_Receive)begin Receive_Baud_Start = 0;Rx_Byte = rx_data;end
053                                        $display("Master_receive Data = %0h",Rx_Byte);
054                                end
055                        end
056                end
057
058        endmodule


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

001        `timescale 1ns/1ps
002
003        module Uart_Tx_Model(Baud_Set,Tx_Data,Tx_En,uart_tx,Tx_Done);
004               
005                input Baud_Set;        /*波特率选择信号*/
006                input Tx_Data;        /*待发送数据字节*/
007                input Tx_En;                        /*数据字节发送使能信号*/
008                output reg uart_tx;        /*仿真串口发送模型发送信号*/
009                output reg Tx_Done;        /*发送完成信号*/
010               
011                reg Clk;        /*仿真模型内部工作时钟*/
012                reg Rst_n;        /*仿真模型内部复位信号*/
013               
014                wire Bps_Clk;        /*发送波特率时钟波特率*/
015                reg Bps_En;        /*发送波特率使能信号*/
016                       
017                initial Clk = 1;
018                always#10 Clk = ~Clk;
019
020        /*----例化发送波特率时钟生成模块-----*/       
021                TxModel_Bps_Gen TxModel_Bps_Gen_send(
022                        .Clk(Clk),
023                        .Rst_n(Rst_n),
024                        .Baud_Set(Baud_Set),
025                        .Tx_Done(Tx_Done),
026                        .Bps_Clk(Bps_Clk),
027                        .Byte_En(Bps_En)
028                );
029                       
030                initial begin
031                        Tx_Done = 0;
032                        uart_tx = 1;
033                        Rst_n = 0;
034                        Bps_En = 0;
035                        #100;
036                        Rst_n = 1;
037                        forever@(posedge Tx_En)/*每来一个发送使能信号即执行一次发送过程*/
038                                Uart_Send(Tx_Data);       
039                end
040
041        /*执行一次字节数据的发送*/       
042                task Uart_Send;
043                        input Data;
044                        begin
045                                Bps_En = 1;
046                                Tx_Done = 0;
047                                $display("Uart_Send Data = %0h",Data);/*打印发送的数据*/
048                                @(posedge Bps_Clk) #0.1 uart_tx = 0;
049                                @(posedge Bps_Clk) #0.1 uart_tx = Data;
050                                @(posedge Bps_Clk) #0.1 uart_tx = Data;
051                                @(posedge Bps_Clk) #0.1 uart_tx = Data;
052                                @(posedge Bps_Clk) #0.1 uart_tx = Data;
053                                @(posedge Bps_Clk) #0.1 uart_tx = Data;
054                                @(posedge Bps_Clk) #0.1 uart_tx = Data;
055                                @(posedge Bps_Clk) #0.1 uart_tx = Data;
056                                @(posedge Bps_Clk) #0.1 uart_tx = Data;
057                                @(posedge Bps_Clk) #0.1 uart_tx = 1;
058                                @(posedge Bps_Clk) #0.1 ;
059                                Tx_Done = 1;
060                                Bps_En = 0;
061                                #20 Tx_Done = 0;
062                        end
063                endtask
064               
065        endmodule

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

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

小梅哥 发表于 2015-4-9 11:45:09

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


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


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


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


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


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

小梅哥 发表于 2015-4-9 11:47:06

需要工程的请关注至芯官方微信号

寻你灯火阑珊处 发表于 2015-4-12 10:04:02

工程是次要的,技术文档体现的方法和思想是关键,从代码也可以学到很好的风格,怒刷赞{:3_46:}

telewuhun 发表于 2015-4-17 16:57:01

太牛逼了,值得学习啊~~

Sunlife 发表于 2015-7-29 09:34:31

                :) 看了你的帖子 瞬间爱上你了怎么办

同人于郊 发表于 2015-8-1 16:37:04

感谢小梅哥的无私分享,小弟邮箱   759896891@qq.com    麻烦了
页: [1]
查看完整版本: 基于ZX-2型FPGA开发板的串口示波器(五)