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

小梅哥   2015-4-8 16:54 楼主
系统仿真验证及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 [2:0]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 [7:0]rx_data;/*接收数据移位寄存器*/
015
016 reg [7:0]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[0] = uart_rx;
045 @(posedge Mid_Flag_Receive)rx_data[1] = uart_rx;
046 @(posedge Mid_Flag_Receive)rx_data[2] = uart_rx;
047 @(posedge Mid_Flag_Receive)rx_data[3] = uart_rx;
048 @(posedge Mid_Flag_Receive)rx_data[4] = uart_rx;
049 @(posedge Mid_Flag_Receive)rx_data[5] = uart_rx;
050 @(posedge Mid_Flag_Receive)rx_data[6] = uart_rx;
051 @(posedge Mid_Flag_Receive)rx_data[7] = 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
[/table]
以下为串口发送仿真模型的设计代码
001 `timescale 1ns/1ps
002
003 module Uart_Tx_Model(Baud_Set,Tx_Data,Tx_En,uart_tx,Tx_Done);
004
005 input [2:0]Baud_Set; /*波特率选择信号*/
006 input [7:0]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 [7:0]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[0];
050 @(posedge Bps_Clk) #0.1 uart_tx = Data[1];
051 @(posedge Bps_Clk) #0.1 uart_tx = Data[2];
052 @(posedge Bps_Clk) #0.1 uart_tx = Data[3];
053 @(posedge Bps_Clk) #0.1 uart_tx = Data[4];
054 @(posedge Bps_Clk) #0.1 uart_tx = Data[5];
055 @(posedge Bps_Clk) #0.1 uart_tx = Data[6];
056 @(posedge Bps_Clk) #0.1 uart_tx = Data[7];
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
[table]
以下为仿真顶层模块的设计
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_WIDTH - 1:0]Key_in;
009
010 reg ADC_Din;
011 wire ADC_Clk;
012 wire ADC_Cs_n;
013
014 /*波特率设置总线,此处默认为9600bps,仿真不做波特率修改测试*/
015 wire [2:0]Baud_Set;
016 reg [7:0]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 [7:0]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[25:0] Fs;/*采样率实际频率*/
193 reg [31:0] 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[15:8],S_cnt_top[7:0]);
199 /*写采样分频计数器计数最大值高16位*/
200 Send_CMD(`ADC_S_Cnt_Max_H,S_cnt_top[31:24],S_cnt_top[23:16]);
201 $display("Set ADC Sample Speed as = %0d" ,Fs);
202 end
203 endtask
204
205 task Set_DDS_Sample_Speed;/*设置DDS数据的采样率*/
206 input[25:0] Fs;/*采样率实际频率*/
207 reg [31:0] 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[15:8],S_cnt_top[7:0]);
213 /*写采样分频计数器计数最大值高16位*/
214 Send_CMD(`DDS_S_Cnt_Max_H,S_cnt_top[31:24],S_cnt_top[23:16]);
215 $display("Set DDS Sample Speed as = %0d" ,Fs);
216 end
217 endtask
218
219 task Set_DDS_Fout_Speed;/*设置DDS输出信号频率*/
220 input[25:0] Fs;/*输出信号实际频率*/
221 reg [31:0] r_fword;/*DDS频率控制字*/
222 begin
223 /*由实际要求输出频率数据换算出频率控制字*/
224 r_fword = Fs*65536*65536/50000000;
225 Send_CMD(`DDS_Fword_L,r_fword[15:8],r_fword[7:0]);
226 Send_CMD(`DDS_Fword_H,r_fword[31:24],r_fword[23:16]);
227 $display("Set DDS Fout as = %0d" ,Fs);
228 end
229 endtask
230
231
232 task press_key;
233 input [KEY_WIDTH/2:0]Key;
234 reg [15:0]myrand;
235 begin
236 Key_in = {KEY_WIDTH{1'b1}};
237 /*按下抖动*/
238 repeat(20)begin
239 myrand = {$random} % 65536;
240 #myrand Key_in[Key] = ~Key_in[Key];
241 end
242 Key_in[Key] = 1'b0;
243
244 #22000000;/*稳定期*/
245
246 /*释放抖动*/
247 repeat(20)begin
248 myrand = {$random} % 65536;
249 #myrand Key_in[Key] = ~Key_in[Key];
250 end
251 Key_in[Key] = 1'b1;
252 #22000000;/*稳定期*/
253 end
254 endtask
255
256 endmodule
下图为系统仿真架构图:
仿真架构.jpg
这里,在我们提供的工程中,已经设置好了Nativelink,用户只需要在QuartusII中点击tools—run rtl simulation tool—rtl simulation即可自动调用modelsim-altera并执行仿真,因为这里完全模拟真实时序进行仿真,因此运行完整个仿真大约需要510分钟。
仿真完成后,结果如图所示:
全局效果图.jpg
其中,Rx_Byte为串口接收仿真模型接收到的数据,这里以波形的方式展示。ADC_DataADC采样结果,DDS_DataDDS输出的数据最下方为按键标志和按键结果,当按下按键1时,数据通道切换为ADC的采样结果,当按下按键2时,数据通道切换为DDS的输出数据。
(如果用户在进行仿真的过程中发现仿真无法运行,在modelsim中提示错误的话,请删除simulation>modelsim文件夹下除wave.domydo.do文件外的其他所有文件,然后在quartus 中重新启动仿真)
小梅哥
201548 于至芯科技
本帖最后由 小梅哥 于 2015-4-8 17:03 编辑

回复评论 (8)

不错的资料!
生活就是油盐酱醋再加一点糖,快活就是一天到晚乐呵呵的忙 =================================== 做一个简单的人,踏实而务实,不沉溺幻想,不庸人自扰
点赞  2015-4-8 17:07
在modelsim中跑出了正弦波
点赞  2015-4-8 18:55
不错不错,注释写的很详细
training
点赞  2015-4-8 20:33
楼上加好友。。
是我没锁好回忆让它四处矫情真是抱歉
点赞  2015-4-8 22:59
太牛逼了!
工程分享一下呗
点赞  2015-4-9 20:11
有点厉害。。。。。
分享铸就美好未来。。。
点赞  2015-4-11 10:36
楼上加好友。。
点赞  2015-4-13 15:01
小梅哥,太厉害了,求分享
点赞  2015-4-13 15:06
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复