FPGA|CPLD|ASIC论坛
直播中

航天萪工

3年用户 7经验值
擅长:可编程逻辑 嵌入式技术 处理器/DSP 控制/MCU
私信 关注

【FPGA开发者项目连载】基于FPGA的数字电路实验验证平台Version1.0

演示视频

本帖最后由 航天萪工 于 2021-5-24 18:25 编辑

(开幕:自己之前玩过一些FPGA,大部分都是ALTERA的XILINX,这次有机会使用到国产的FPGA还是很开心的,尤其这款芯片还有个嵌入的ARM硬核处理器,虽然这次设计没有用到。
PART1:我在本科的时候上数字电路实验课,使用的CYCLONE2片子,老师会留一些实验内容,什么3-8译码器,加法器,乘法器或者是分频器,数码管显示学号,最后大作业是做一个数字时钟,并且可以加速的那种。当时做实验因为第一次用,很多都不会,手忙脚乱的,老师也不怎么管,大佬们自己写,我们这种菜鸡就只能自己上网找或者等着大佬们做完直接抄,。倒也不是自己懒,就是单纯因为时间有限,而自己又不知道问题出在哪里,再加上学校也不是很重视,所以就变得越来越水。后来自己入门了FPGA,想着自己做一个验证平台,不需要麻烦老师,自己就可以根据平台给的提示完成实验。
PART2:我就是个渣渣,所以很多代码写的不规范或者很垃圾,还望大神们手下留情,也欢迎大家一起讨论FPGA,我自己在学的时候就有很多问题因为身边没有人交流导致入门很坎坷。软件部分,QT就随便一学,满足日常开发的需求就可以了,所以这次设计的QT特别垃圾,希望有大神可以帮帮我,我C++都是当c用的,,,因为不太会。)
感谢大家打开我这篇帖子,希望对你有所帮助,并欢迎大家评论指出问题。
一、设计思路:
       在高校的数字电路课程中,要通过在FPGA器件上通过设计一些简单的时序或者组合逻辑电路深入学习与验证,但是每个人的做完的结果会有各种问题与错误,大大增加了老师的工作量,因此准备设计一个基于FPGA的验证模块,该模块可以通过插入到设计者的模块中,并使用探针采集数据并做初步分析,结果上传到上位机软件中,在上位机中分析结果,通过与预定的答案进行分析比对,给出得分,如果结果错误给出相应的错误原因与改正方法。
为了方便对采集结果快速分析,因此需要在上位机软件中进行,上位机预计采用QT平台开发,通过串口进行数据交互。上位机中将事先预制好要测试的题目或功能,同时会预留出扩展接口,可以自定义测试功能与方法,将测试内容下发至FPGA中,并通过与用户功能接口连接,下载至FPGA中根据上位机指令开始测试并在上位机中显示交互数据与结果。
二、实现目标:
1、该采集模块可以对用户设计的频率完成测量,频率范围1K~10MHz,采用等精度法测频;
2、该采集模块可以对用户设计的四位(至多八位)静态数码管完成显示数据采集,并上传至上位机判别实际显示内容与错误原因;
3、该采集模块可以对用户设计的四位(至多八位)动态显示数码管完成显示数据采集,并上传至上位机判别是否在指定时间内完成正确数据显示,并对错误内容给出定位与解决方法;
4、该采集模块可以对用户设计的基本逻辑单元(如3-8译码器、加法器等)完成输入与输出采集,并将采集结果上传至上位机中进行判别,并对错误内容给出错误定位与解决方法;
5、该采集模块可以完成用户设计的数码管时钟显示实验完成不同时刻的数据采集,并将采集结果上传至上位机中,对设定的时标的显示内容进行判别,并对错误内容给出定位与解决方法。
三、目前完成内容:
1、完成对用户设计的模块的频率采集与测量,使用等精度测量法实现,并将采集数据通过串口上传至上位机;
2、完成对用户设计的四位静态数码管显示内容的采集,并将要显示的内容与实际的显示内容通过串口上传至上位机中进行判别;
3、上位机采用QT C++开发,可以后续对上位机功能进行添加,但是对于相应错误的判别代码未完成,待下一版完成;
4、本设计的全部模块均采用RTL逻辑设计,包括串口部分,方便在不同的FPGA平台上移植和使用。
四、硬件顶层设计框图:
     系统总体设计框图如图4.1所示:
图4_1.png
图4.1硬件总体框图
五、各模块设计说明:
1、等精度测频模块:
       等精度测量的一个最大特点是测量的实际门控时间不是一个固定值,而是一个与被测信号有关的值,刚好是被测信号的整数倍。在计数允许时间内,同时对标准信号和被测信号进行计数,再通过数学公式推导得到被测信号的频率。由于门控信号是被测信号的整数倍,就消除了对被测信号产生的±l周期误差,但是会产生对标准信号±1周期的误差。等精度测量原理如图5.1.1所示。
图5_1_1.png
图5.1.1等精度测频原理
       根据等精度测频原理,设计的代码框图如下图5.1.2所示:
图5_1_2.png
图5.1.2等精度测频代码设计
       产生预置闸门代码如下所示:
  1. always @(posedge I_sys_clk or negedge I_rst_n) begin
  2.     if(!I_rst_n) begin
  3.         gate_cnt <= 32'd0;
  4.         R_done_pre <= 0;
  5.         gate_time_pre <= 0;
  6.         R_start <= 0;
  7.     end
  8.     else begin
  9.         if(W_start_pos) begin
  10.             R_start <= 1;
  11.         end
  12.         if(R_start) begin
  13.             if(gate_cnt == SET_1S_PERD) begin
  14.                 gate_time_pre <= 0;
  15.                 R_done_pre <= 1;
  16.                 R_start <= 0;
  17.             end
  18.             else begin
  19.                 gate_cnt <= gate_cnt + 32'd1;
  20.                 gate_time_pre <= 1;
  21.                 R_done_pre <= 0;
  22.             end
  23.         end
  24.         else begin
  25.             gate_time_pre <= 0;
  26.             gate_cnt <= 32'd0;
  27.             R_done_pre <= R_done_pre;
  28.         end
  29.     end
  30. end




       同步预置闸门到待测时钟域代码如下所知:
  1. reg gate_time;

  2. always @(posedge I_clk_fx or negedge I_rst_n) begin
  3.     if(!I_rst_n) begin
  4.         gate_time <= 0;
  5.     end
  6.     else begin
  7.         gate_time <= gate_time_pre;
  8.     end
  9.    
  10. end


       基准信号与待测信号技术代码如下所示:
  1. assign O_done = ({R_done_pre,gate_time} == 2'b10) ? 1 : 0;

  2. always @(posedge I_clk_fx or negedge I_rst_n) begin
  3.     if((!I_rst_n)) begin
  4.         O_fx_cnt <= 32'd0;
  5.     end
  6.     else begin
  7.         if(gate_time) begin
  8.             O_fx_cnt <= O_fx_cnt + 32'd1;
  9.         end
  10.         else begin
  11.             O_fx_cnt <= O_fx_cnt;
  12.         end
  13.     end
  14. end

  15. always @(posedge I_sys_clk or negedge I_rst_n) begin
  16.     if((!I_rst_n)) begin
  17.         O_f0_cnt <= 32'd0;
  18.     end
  19.     else begin
  20.         if(gate_time) begin
  21.             O_f0_cnt <= O_f0_cnt + 32'd1;
  22.         end
  23.         else begin
  24.             O_f0_cnt <= O_f0_cnt;
  25.         end
  26.     end
  27. end



2、数码管采集模块:
       数码管分为共阴极与共阳极数码管,本次设计是对共阳极数码管进行测试,对于共阴极数码管只需要修改上位机预存储的标准模板即可。
       数码管分为段选和位选。本次设计只完成了四位数码管的采集,数码管的采集原理是根据每次位选信号的变化,采集所有的段选数据,待四个位选轮流一次后,将采集到的四组段选数据和需要显示的数据通过端口发送至上位机中,在上位机中对所所需要显示的数据与采集到的段选数据对比,并根据结果返回判别结果。设计框图如图5.2.1所示。
图5_2_1.png
图5.2.1数码管采集模块代码设计
       对每次位选变化采集段选数据的代码片段:
  1. always @(posedge I_sys_clk or negedge I_rst_n) begin
  2.     if(~I_rst_n) begin
  3.         R_test_num <= 0;
  4.         O_data1 <= 0;
  5.         O_data0 <= 0;
  6.         R_01_flag <= 1'b0;
  7.         R_02_flag <= 1'b0;
  8.         R_03_flag <= 1'b0;
  9.         R_04_flag <= 1'b0;
  10.     end
  11.     else begin
  12.         if(R_start_en & I_test_en) begin
  13.             case (I_test_sel)
  14.                 4'b0001 : begin
  15.                     O_data1[15:0] <= I_test_disp_data;
  16.                     O_data0[7:0] <= I_test_seg;
  17.                     R_01_flag <= 1'b1;
  18.                     if(R_01_flag) begin
  19.                         R_test_num <= R_test_num;
  20.                     end
  21.                     else begin
  22.                         R_test_num <= R_test_num + 1;
  23.                     end
  24.                     
  25.                 end
  26.                 4'b0010 : begin
  27.                     O_data0[15:8] <= I_test_seg;
  28.                     R_02_flag <= 1'b1;
  29.                     if(R_02_flag) begin
  30.                         R_test_num <= R_test_num;
  31.                     end
  32.                     else begin
  33.                         R_test_num <= R_test_num + 1;
  34.                     end
  35.                 end
  36.                 4'b0100 : begin
  37.                     O_data0[23:16] <= I_test_seg;
  38.                     R_03_flag <= 1'b1;
  39.                     if(R_03_flag) begin
  40.                         R_test_num <= R_test_num;
  41.                     end
  42.                     else begin
  43.                         R_test_num <= R_test_num + 1;
  44.                     end
  45.                 end
  46.                 4'b1000 : begin
  47.                     O_data0[31:24] <= I_test_seg;
  48.                     R_04_flag <= 1'b1;
  49.                     if(R_04_flag) begin
  50.                         R_test_num <= R_test_num;
  51.                     end
  52.                     else begin
  53.                         R_test_num <= R_test_num + 1;
  54.                     end
  55.                 end
  56.             default: O_data0 <= O_data0;
  57.             endcase
  58.         end
  59.         else  begin
  60.             if(I_test_en) begin
  61.                 R_test_num <= R_test_num;
  62.                 O_data1 <= O_data1;
  63.                 O_data0 <= O_data0;
  64.             end
  65.             else begin
  66.                 R_test_num <= 0;
  67.                 O_data1 <= 0;
  68.                 O_data0 <= 0;
  69.                 R_01_flag <= 1'b0;
  70.                 R_02_flag <= 1'b0;
  71.                 R_03_flag <= 1'b0;
  72.                 R_04_flag <= 1'b0;
  73.             end
  74.         end
  75.     end
  76. end


3、串口模块:
       该串口模块完全自己完成,可以在波特率产生模块设定所需要的波特率,且对于串口发送部分有了发送使能与发送完成信号,接收部分有接收完成信号。由于接收模块额外增加了一个时钟周期产生接收完成信号,因此对于串口上位机连续发送的接收时会有问题,需要插入一定的延时,具体操作见上位机设计,串口部分模块如图5.3.1所示。
图5_3_1.png
图5.3.1串口模块框图
       详细代码见源代码部分。
4、控制模块顶层设计:
       为了封装各个测量部分,并接收上位机下发的指令与上传采集结果,需要设计一个整体的控制单元,该单元主要完成接收上位机下发的数据,判断进行那些测量,并对对应的模块发送使能信号,待该模块测量完成后,通过串口再次将数据上发至上位机中,供上位机使用,
该模块采用状态机,如图5.4.1所示:
图5_4_1.png
图5.4.1控制模块状态机图
       在控制模块中,每次发送是64bit,接收是72bit,上位机发送的数据帧头为0xEC,帧尾为0xA5,中间的数据为实际要进行的测量内容,01表示测量频率,02表示测量数码管。FPGA发送帧头为0x5A,其他64bit为测量数据,具体内容根据不同测量内容有有异。
六、上位机设计:
1、软件界面:
       上位机软件界面如图6.1.1所示:
图6_1_1.png
图6.1.1上位机软件界面
       目前上位机可完成的测试仅有频率测量与静态数码管显示,但是后期可以进行功能的更新。
2、部分源代码展示:
       由于FPGA串口接收设计中,额外增加了一个接收完成信号,导致无法连续接收数据,必须等待一个周期后再次发送,因此上位机中在每次发送插入了一定的延时,代码片段如下所示:
  1. HeadByte[0] = 0xEC;
  2.     HeadByte[1] = 0x01;
  3.     HeadByte[2] = 0x00;
  4.     HeadByte[3] = 0x00;
  5.     HeadByte[4] = 0x00;
  6.     HeadByte[5] = 0x00;
  7.     HeadByte[6] = 0x00;
  8.     HeadByte[7] = 0xa5;

  9. void Widget::on_pushButtonP1_clicked()
  10. {

  11.     HeadByte[1] = 0x01;

  12.     //serialport->write(HeadByte);

  13.     QByteArray tmp;
  14.     tmp.resize(1);
  15.     for(int idx = 0;idx < 8;idx++){
  16.         tmp[0] = HeadByte.at(idx);
  17.         serialport->write(tmp);
  18.         Sleep(10);
  19.     }

  20.     ui->pushButtonP1->setEnabled(false);
  21.     ui->pushButtonP2->setEnabled(false);
  22.     flag_state = 1;
  23.     ui->textCommd->append("Frequence measure cmd has sended!n");

  24. }




七、测试:
1、频率测试:
待测时钟源采用FPGA内部的PLL产生50MHz时钟,如图 测1所示,同时使用分频的方式产生待测时钟,如图 测2所示,分频系数如图 测3所示为100KHz,测4为148KHz,引脚分配如图 测5 ,连接实物如图 测6所示:
总时钟源.png
测1 FPGA PLL 50MHz时钟源

分频代码.png
测2 分频代码

分频系数.png
测3 100KHz分频系数

167分频.png
测4 148KHz分频系数

时钟引脚输出.png
测5 引脚分配

频率测试实物.jpg
测6 实物连接图
1)100K频率测试:
       如图7.1.1,设置基准时钟50MHz,用户设计时钟100K,测量结果如图7.1.2所示,ADALM2000测试结果如图 测7:
图7_1_1.png
图7.1.1用户设计时钟100KHz
图7_1_2.png
图7.1.2上位机测量结果为100KHz
100K实测.png
测7 ADALM2000测试结果
2)149K频率测试:
       如图7.1.3,用户设计时钟149KHz,测量结果如图7.1.4所示,ADALM2000测试结果如图 测8
图7_1_3.png
图7.1.3用户设计时钟149KHz
图7_1_4.png
图7.1.4上位机测量结果为148.8KHz
148K实测.png
测8 ADALM2000测试结果
2、数码管显示测试:
引脚分配如图 测9,实物连接图如图 测10所示:
数码管引脚分配.png
测9 数码管输出引脚分配
数码管实物实测.jpg
测10 实物图
1)正确显示的数码管测试:
设置要显示0xEC1D,如图7.2.1所示,上位机测试结果如图7.2.2所示,ADALM2000的逻辑分析仪抓取结果如图 测11所示:
图7_2_1.png
图7.2.1GOWIN编辑器
图7_2_2.png
图7.2.2上位机返回结果
数码管输出波形实测.png
测11 ADALM2000测试结果
八、待改进部分:
1、将未完成部分继续完成;
2、重构QT上位机程序;
3、增加一些存储单元对采集数据进行暂存;
4、修改控制模块逻辑,并且给串口收发增加FIFO,完善数据包格式;
5、etc
九、源代码:

见github或附件。
https://github.com/kevinliuyunfeng/GOWIN_FPGA.git

附件1:FPGA工程
GOWIN_PRJ.zip (294.14 KB)
(下载次数: 5, 2021-5-8 13:37 上传)
附件2:QT工程
QTHostcomFPGA.rar (957.21 KB)
(下载次数: 1, 2021-5-8 13:38 上传)

演示视频2

回帖(4)

负或___

2021-5-8 16:02:34
太强了,顶一波
2 举报

李丹

2021-5-8 19:23:23
很棒
1 1 举报

Ghoster

2021-5-9 11:04:04
师兄太强了~
1 1 举报

黄先

2021-5-12 16:41:36
来看了。。加油。。。。
1 举报

更多回帖

发帖
×
20
完善资料,
赚取积分