guyu_1

个性签名:我与fpga的那些事儿http://blog.sina.com.cn/u/5707446562

  • 2018-12-05
  • 发表了主题帖: 数字时钟-明德扬至简设计与应用FPGA

      1    项目背景  技术交流群:544453837         数字时钟用数字电路技术实现时、分、秒计时显示的装置。用数字同时显示时,分,秒的精确时间,并能实现准确校时。与传统表盘式机械式时钟相比,具有更高的准确性和直观性,且无机械装置,具有更长的使用寿命。不仅如此,还具备体积小、重量轻、抗干扰能力强、对环境要求高、高精确性、容易开发等特性。本案例将详细介绍用至简设计法实现数字时钟的功能。2    设计目标       本工程使用6个数码管实现数字时钟功能。该功能与数字时钟相同,即从00:00:00一直到23:59:59。       上板效果图如下图所示。http://s10.sinaimg.cn/mw690/007n4XDCzy7p0qjxfDz39 3    设计实现3.1    顶层信号      新建目录:D:\mdy_book\my_shizhong。在该目录中,新建一个名为my_shizong.v的文件,并用GVIM打开,开始编写代码。      我们要实现的功能,概括起来就是控制8个数码管,让其中2个数码管常灭,其他6个数码显示不同的数字。要控制8个数码管,就需要控制位选信号,即FPGA要输出一个8位的位选信号,设为seg_sel,其中seg_sel[0]对应数码管0,seg_sel[1]对应数码管1,以此类推,seg_sel[7]对应数码管7。       要显示不同的数字,就需要控制段选信号,不需要用到DP,一共有7根线,即FPGA要输出一个7位的段选信号,设为seg_ment,seg_ment[6]~segm_ment[0]分别对应数码管的abcdefg(注意对应顺序)。       我们还需要时钟信号和复位信号来进行工程控制。       综上所述,我们这个工程需要4个信号,时钟clk,复位rst_n,输出的位选信号seg_sel和输出的段选信号seg_ment。http://s16.sinaimg.cn/mw690/007n4XDCzy7p0qlLrVJ7f       将module的名称定义为my_shizhong。并且我们已经知道该模块有4个信号:clk、rst_n、seg_sel和seg_ment,代码如下:http://s3.sinaimg.cn/mw690/007n4XDCzy7p0qnKgQG42        其中clk、rst_n是1位的输入信号,seg_sel是8位的输出信号,seg_ment是7位的输出信号,根据此,补充输入输出端口定义。代码如下:http://s8.sinaimg.cn/mw690/007n4XDCzy7p0qpOxjF87 3.2   信号设计      我们先分析要实现的功能,我们用m_g,m_s,f_g,f_s,s_g,s_s分别表示秒钟个位、秒钟十位、分钟个位、分钟十位、小时个位和小时十位所表示的数字值,m_g_s,m_s_s,f_g_s,f_s_s,s_g_s,s_s_s分别表示秒钟个位、秒钟十位、分钟个位、分钟十位、小时个位和小时十位所表示数码管段选值。      数码管0显示的是秒个位值,则翻译成信号就是seg_sel的值为8’b1111_1110,seg_ment的值为:m_g_s。数码管1显示秒十位,也就是说seg_sel的值为8’b1111_1101,seg_ment的值为m_s_s。以此类推,数码管5显示小时十位,也就是seg_sel的值为8’b1101_1111,seg_ment的值为s_s_s。 http://s15.sinaimg.cn/mw690/007n4XDCzy7p0qtdAKy7e       那么seg_ment和seg_sel多久变化一次呢?我们就需要用到数码管动态扫描原理了。       数码管动态显示接口是应用最为广泛的一种显示方式之一,动态驱动是将所有数码管的8个显示笔划"a,b,c,d,e,f,g,dp"的同名端连在一起,另外为每个数码管的公共极COM增加位选通控制电路,位选通由各自独立的I/O线控制,当要输出字形码时,所有数码管都接收到相同的字形码,但究竟是哪个数码管会显示出字形,取决于单片机对位选通COM端电路的控制,所以我们只要将需要显示的数码管的选通控制打开,该位就显示出字形,没有选通的数码管就不会亮。通过分时轮流控制各个数码管的的COM端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的I/O端口,而且功耗更低。      也就是说,只要刷新时间为1~2ms,那么人眼看起来就似乎是所有数码管都在显示。因为我们把这个刷新时间定为2ms,也就是如下图。 http://s5.sinaimg.cn/mw690/007n4XDCzy7p0qu5lT6a4       由波形图可知,我们需要1个计数器用来计算2毫秒的时间。本工程的工作时钟是50MHz,即周期为20ns,计数器计数到2_000_000/20=100_000个,我们就能知道2毫秒时间到了。另外,由于该计数器是不停地计数,永远不停止的,可以认为加1条件一直有效,可写成:assignadd_cnt0==1。综上所述,该计数器的代码如下。http://s5.sinaimg.cn/mw690/007n4XDCzy7p0qwHStue4       再次观察波形图,我们发现依次是显示的是m_g_s、m_s_s、f_g_s、f_s_s、s_g_s和s_s_s,一共6个,循环进行显示。这说明还需要另外一个计数器来表示第几个。该计数器每隔2ms加1,因此加1条件为end_cnt0。该计数器一共要数6次。所以代码为:http://s9.sinaimg.cn/mw690/007n4XDCzy7p0qB7Vb2e8       有了两个计数器,我们来思考输出信号seg_sel的变化。概括起来,在第1次的时候输出值为8’hfe;在第2次的时候输出值为8’hfd;以此类推,在第6次的时候输出值为8’hdf。我们用信号cnt1来代替第几次,也就是:当cnt1==0的时候,输出值为8’hfe;在cnt1==1的时候输出值为8’hfd;以此类推,在cnt1==5的时候输出值为8’hdf。再进一步翻译成代码,就变成如下:http://s1.sinaimg.cn/mw690/007n4XDCzy7p0qCusGQ80       或者可以简写成:http://s2.sinaimg.cn/mw690/007n4XDCzy7p0qDu4tX11       对上面代码解释一下,第131行是指先将8’b1向左移位,再取反后的值,赋给seg_sel。假设此时cnt1等于0,那么8’b1<<0的结果是8’b0000_0001,取反的值为8’hfe;假设cnt1等于3,那么8’b1<<3的结果为8’b000_1000,取反后的结果为8’b1111_0111,即8’hf7。与第一种写法的结果都是相同的。      我们来思考输出信号seg_ment的变化。seg_ment的值,是m_g、m_s、f_g、f_s、s_g和s_s等数值分别译码成数码管显示的信号m_g_s、m_s_s、f_g_s、f_s_s、s_g_s和s_s_s。我们一种做法,是将数值分别转成译码信号,然后再挑选出来给seg_ment。如下面的原理图:http://s13.sinaimg.cn/mw690/007n4XDCzy7p0qGyi4s2c       我们还可以是先可以选择哪一组数据,选出来后再做译码,其原理图如下:http://s2.sinaimg.cn/mw690/007n4XDCzy7p0qHrqJXa1        对比两个原理图,可以发现,实现同样的功能,第二个原理图比第一个,少了5个译码电路。这是一个简单的例子,说明实现一个功能,可以有很多种方法,方法各有优势,能用较少的资源、较快的速度实现功能,这就是设计师的能力,也是FPGA设计的魅力所在。       我们采用第二种实现方案。设计一个信号sel_data,表示从6个数据中选择的信号,之后再做译码。波形图更新如下:http://s10.sinaimg.cn/mw690/007n4XDCzy7p0qJLFa1a9       sel_data的值有可能为0~9中的任意数字,这些数字都要转成数码管的段选信号。下表列出不同数字对应的段选信号值。http://s12.sinaimg.cn/mw690/007n4XDCzy7p0qN3zWreb       明德扬开发板使用的是共阳数码管。根据上表,可写出下面代码。http://s10.sinaimg.cn/mw690/007n4XDCzy7p0qS4oPL69       当然,也可以写成case的形式,结果都是一样的。http://s13.sinaimg.cn/mw690/007n4XDCzy7p0qVy2oQcc       sel_data从m_g、m_s、f_g、f_s、s_g和s_s中选取。当cnt1=0,即数码管0显示时,sel_data的值为m_g;当cnt1=1,即数码管1显示时,sel_data的值为m_s;当cnt1=2,即数码管2显示时,sel_data的值为f_g;当cnt1=3,即数码管3显示时,sel_data的值为f_s;当cnt1=4,即数码管4显示时,sel_data的值为s_g;当cnt1=5,即数码管5显示时,sel_data的值为s_s。为此,sel_data的代码为:http://s3.sinaimg.cn/mw690/007n4XDCzy7p0r2ZfFMb2       接下来我们设计m_g、m_s、f_g、f_s、s_g和s_s信号,按照常识,秒个位m_g的变化规律如下:http://s10.sinaimg.cn/mw690/007n4XDCzy7p0r3ZB7Hd9       秒个位的数据是0、1、2~9、0这样有规律的加1变化,很明显可以看出这个m_g其实就是一个计数器,并且这个计数器每隔1秒时间才变化加1一次。1秒时间计时也需要一个计数器,设为cnt2,则cnt2是一直加1的,即可以写成assign add_cnt2 = 1。cnt2要数1秒时间,本工程的工作时钟是50MHz,即周期为20ns,计数器计数到1_000_000_000/20=50_000_000个,我们就能知道1秒时间到了,所以cnt1的周期是50_000_000个。有了cnt1,我们就知道m_g这个计数器的加1条件是1秒时间到了,即end_cnt2==1,要计数10个。综上所述,可以写出cnt2和m_g的代码如下:http://s13.sinaimg.cn/mw690/007n4XDCzy7p0r94FqYbc      接下来思考秒十位m_s,m_s的变化情况如下: http://s2.sinaimg.cn/mw690/007n4XDCzy7p0ra8wPn71       m_s秒十位的指示,很明显每隔10秒就会加1,很明显,m_s也是一个计数器,加1条件是10秒时间到,也就是end_m_g。该计数器周期性是数6个。所以代码如下: http://s5.sinaimg.cn/mw690/007n4XDCzy7p0ApMNoMd4        接下来思考分个位f_g,f_g的变化情况如下:http://s7.sinaimg.cn/mw690/007n4XDCzy7p0ArktDw16 http://s7.sinaimg.cn/mw690/007n4XDCzy7p0HRC2bA16 ​      接下来思考分十位f_s,f_s的变化情况如下:http://s1.sinaimg.cn/mw690/007n4XDCzy7p0HU9oyY30 ​      f_s分十位的指示,很明显每隔10分钟就会加1,很明显,f_s也是一个计数器,加1条件是10分钟到,也就是end_f_g。该计数器周期性是数6个。所以代码如下: http://s2.sinaimg.cn/mw690/007n4XDCzy7p0I0Et7bf1       接下来思考小时个位s_g,s_g的变化情况如下:http://s13.sinaimg.cn/mw690/007n4XDCzy7p0I22tsM7c ​      s_g小时个位的指示,很明显每隔1小时即6分钟就会加1,很明显,s_g也是一个计数器,加1条件是1小时也就是60分钟时间到,也就是end_f_g。      接下来我们需要好好思考这个小时个位,它的周期是多少。有读者认为是10个,因为是小时个位会从0~9这样变化;有读者认为是24个,因为一共是24小时;有读者认为是4个,因为小时个位会从0~4变化。      认为是24小时的读者,没有认识到我们只是看小时个位的变化,而不是小时个位和小时十位整体,整体才是24小时。我们盯着小时个位,会发现它是这么一个规律:0~9,0~9,0~3,0~9,0~9,0~3。因此正确的答案是,有时候周期是10,有时候是4,也就是说周期性会变的。按照明德扬的变量法,设其周期是x,则可以写出s_g的代码如下,而x是怎么来,我们先不考虑,后期再说。http://s10.sinaimg.cn/mw690/007n4XDCzy7p0I4rMQFe9      接下来思考小时十位s_s,s_s的变化情况如下:http://s16.sinaimg.cn/mw690/007n4XDCzy7p0I6uBVJff ​      s_s小时十位的指示,很明显当小时个位要清零时(注意不是每隔10小时),s_s就会加1,很明显,s_s也是一个计数器,加1条件是小时个位要清零了,也就是end_s_g。该计数器周期性是数3个。所以代码如下:http://s2.sinaimg.cn/mw690/007n4XDCzy7p0I9vMaZ01 ​      最后,我们再考虑x,也就是小时个位的周期性是多少,并取决于什么因素。我们可以发现,小时个位是0~9,0~9,0~3,0~9,0~9,0~3,也就是周期性是10或者是4。至于是10还是4,则取决于小时十位,即s_s。当时钟的小时十位显示为2时,小时个位只要数到3就行了,没有25点的。综上所术,发s_s为2时,x=4,否则x=10。所以x的代码如下:http://s15.sinaimg.cn/mw690/007n4XDCzy7p0IazFwO1e ​此次,主体程序已经完成。接下来是将module补充完整。 3.3   信号定义       接下来定义信号类型。      cnt0是用always产生的信号,因此类型为reg。cnt0计数的最大值为100_000,需要用17根线表示,即位宽是17位。add_cnt0和end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:http://s14.sinaimg.cn/mw690/007n4XDCzy7p0JkRWwt3d       cnt1是用always产生的信号,因此类型为reg。cnt1计数的最大值为6,需要用3根线表示,即位宽是3位。add_cnt1和end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下:http://s15.sinaimg.cn/mw690/007n4XDCzy7p0Jm7peede       seg_sel是用always方式设计的,因此类型为reg,其一共有8根线,即位宽为8。因此代码如下:http://s3.sinaimg.cn/mw690/007n4XDCzy7p0JmYzmi52 ​      seg_ ment是用always方式设计的,因此类型为reg,其一共有7根线,即位宽为7。因此代码如下:http://s15.sinaimg.cn/mw690/007n4XDCzy7p0JolsQude ​      sel_data是用always设计的,所以类型为wire。其最大值为9,所以需要4位位宽。代码如下: http://s6.sinaimg.cn/mw690/007n4XDCzy7p0JpFXXn75       cnt2是用always产生的信号,因此类型为reg。cnt2计数的最大值为50_000_000,需要用26根线表示,即位宽是26位。add_cnt2和end_cnt2都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下: http://s1.sinaimg.cn/mw690/007n4XDCzy7p0JtHijm50        m_g是用always产生的信号,因此类型为reg。m_g计数的最大值为9,需要用4根线表示,即位宽是4位。add_m_g和end_m_g都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:http://s4.sinaimg.cn/mw690/007n4XDCzy7p0JuNVpV63 ​      m_s是用always产生的信号,因此类型为reg。m_s计数的最大值为5,需要用3根线表示,即位宽是3位。add_m_s和end_m_s都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:http://s14.sinaimg.cn/mw690/007n4XDCzy7p0JwKy1T6d ​      f_g是用always产生的信号,因此类型为reg。f_g计数的最大值为9,需要用4根线表示,即位宽是4位。add_f_g和end_f_g都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:http://s16.sinaimg.cn/mw690/007n4XDCzy7p0Jzq3cb3f ​      f_s是用always产生的信号,因此类型为reg。f_s计数的最大值为5,需要用3根线表示,即位宽是3位。add_f_s和end_f_s都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:http://s12.sinaimg.cn/mw690/007n4XDCzy7p0JAjwrpab ​      s_g是用always产生的信号,因此类型为reg。s_g计数的最大值为9,需要用4根线表示,即位宽是4位。add_s_g和end_s_g都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:http://s5.sinaimg.cn/mw690/007n4XDCzy7p0JFQB6I04 ​      s_s是用always产生的信号,因此类型为reg。s_s计数的最大值为2,需要用2根线表示,即位宽是2位。add_s_s和end_s_s都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下: http://s1.sinaimg.cn/mw690/007n4XDCzy7p0JHOzuge0        x是用always产生的信号,因此类型为reg。x计数的最大值为10,需要用4根线表示,即位宽是4位。http://s8.sinaimg.cn/mw690/007n4XDCzy7p0JJkCKX07 ​      至此,整个代码的设计工作已经完成。 http://s6.sinaimg.cn/mw690/007n4XDCzy7p0JLg3LDe5 http://s15.sinaimg.cn/mw690/007n4XDCzy7p0JLytKm6e http://s14.sinaimg.cn/mw690/007n4XDCzy7p0JMkMnrad http://s7.sinaimg.cn/mw690/007n4XDCzy7p0JMOvQ266 http://s15.sinaimg.cn/mw690/007n4XDCzy7p0JN4qce2e http://s6.sinaimg.cn/mw690/007n4XDCzy7p0JNQX2Je5 http://s6.sinaimg.cn/mw690/007n4XDCzy7p0JOtdU9e5 http://s16.sinaimg.cn/mw690/007n4XDCzy7p0JPqacf5f http://s9.sinaimg.cn/mw690/007n4XDCzy7p0JR2Acwb8 http://s13.sinaimg.cn/mw690/007n4XDCzy7p0JS4A8sbc http://s9.sinaimg.cn/mw690/007n4XDCzy7p0JSDiNq48 http://s2.sinaimg.cn/mw690/007n4XDCzy7p0JTEE0Ne1 ​      下一步是新建工程和上板查看现象。4.4     综合与上板 4.1    新建工程       首先在d盘中创建名为“my_shizhong”的工程文件夹,将写的代码命名为“my_shizhong.v”,顶层模块名为“my_shizhong”。http://s6.sinaimg.cn/mw690/007n4XDCzy7p0L2UGvr45 http://s8.sinaimg.cn/mw690/007n4XDCzy7p0L3E7rx57       然后打开QuartusⅡ,点击File下拉列表中的New Project Wzard...新建工程选项。http://s14.sinaimg.cn/mw690/007n4XDCzy7p0L4DfBjed ​      3.再出现的界面中直接点击Next。http://s8.sinaimg.cn/mw690/007n4XDCzy7p0L6qIC317 ​      4.之后出现的是工程文件夹、工程名、顶层模块名设置界面。按照之前的命名进行填写,然后点击Next,在出现的界面选择empty project。http://s14.sinaimg.cn/mw690/007n4XDCzy7p0L7G7gxdd http://s10.sinaimg.cn/mw690/007n4XDCzy7p0L8x3L389 ​      5.之后是文件添加界面。添加之前写的“my_shizhong.v”文件,点击右侧的“Add”按钮,之后文件还会出现在下方大方框里,之后点击“Next”。http://s5.sinaimg.cn/mw690/007n4XDCzy7p0LaxMUsa4 ​      器件型号选择界面。在上方红色箭头处选择CycloneⅣE,在中间红色箭头处选择EP4CE15F23C8,然后点击“Next”。http://s13.sinaimg.cn/mw690/007n4XDCzy7p0Lbtc4Ibc ​      EDA工具界面。直接点击“Next”。http://s1.sinaimg.cn/mw690/007n4XDCzy7p0LcA8Io20 ​      8.之后出现的界面,点击“Finish”。http://s6.sinaimg.cn/mw690/007n4XDCzy7p0LdLXjn95 4.2  综合      1.新建工程步骤完成后,就会出现以下界面。选中要编译的文件,点击编译按钮。http://s7.sinaimg.cn/mw690/007n4XDCzy7p0OdPYVg46       2.编译成功后会出现一下界面。http://s9.sinaimg.cn/mw690/007n4XDCzy7p0Ogsy3Cc8 ​4.3  配置管脚http://s10.sinaimg.cn/mw690/007n4XDCzy7p0OhW7Cpe9 ​      在菜单栏中,选中Assignments,然后选择Pin Planner,就会弹出配置管脚的窗口。http://s13.sinaimg.cn/mw690/007n4XDCzy7p0Ojo6zW7c ​     在配置窗口最下方中的location一列,参考下表中最右两列配置好FPGA管脚。http://s15.sinaimg.cn/mw690/007n4XDCzy7p0Okt2Q63e ​4.4   再次综合http://s3.sinaimg.cn/mw690/007n4XDCzy7p0OsIAbof2 ​      在菜单栏中,选中Processing,然后选择Start Compilation,再次对整个工程进行编译和综合。http://s13.sinaimg.cn/mw690/007n4XDCzy7p0OuNneA7c ​      出现上面的界面,就说明编译综合成功。4.5    连接开发板     图中,下载器接入电脑USB接口,电源接入电源,然后摁下蓝色开关。http://s15.sinaimg.cn/mw690/007n4XDCzy7p0OyjgJM1e 4.6   上板      1.双击Tasks一栏中”Program Device”。http://s12.sinaimg.cn/mw690/007n4XDCzy7p0OAREzF1b ​      2.会出现如下界面,点击add file添加.sof文件,点击“Start”,会在“Progress”出显示进度。http://s9.sinaimg.cn/mw690/007n4XDCzy7p0OBB5gc68 ​3.进度条中提示成功后,即可在开发板上观察到相应的现象。$('flv_Era').innerHTML=(AC_FL_RunContent('width', '640', 'height', '498', 'allowNetworking', 'internal', 'allowScriptAccess', 'never', 'src', 'https://v.qq.com/iframe/player.html?vid=h0503qlbwt6&tiny=0&auto=0', 'quality', 'high', 'bgcolor', '#ffffff', 'wmode', 'transparent', 'allowfullscreen', 'true')); 此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2018-12-03
  • 发表了主题帖: VGA显示圆-至简设计与应用FPGA

    1    项目背景  技术交流群:544453837、       在一个平面内,一动点以一定点为中心,以一定长度为距离旋转一周所形成的封闭曲线叫做圆。圆有无数个点。      在同一平面内,到定点的距离等于定长的点的集合叫做圆。圆可以表示为集合{M||MO|=r},圆的标准方程是(x - a) 2 + (y - b) 2 = r 2。其中,o是圆心,r 是半径。http://s15.sinaimg.cn/mw690/007n4XDCzy7p28WK6Xc9e       在平面直角坐标系中,以点O(a,b)为圆心,以r为半径的圆的标准方程是(x-a)2+(y-b)2=r2。      特别地,以原点为圆心,半径为r(r>0)的圆的标准方程为x2+y2=r2。2     设计目标     通过VGA连接线,将显示器和教学板的VGA接口相连。连接示意图如下。http://s12.sinaimg.cn/mw690/007n4XDCzy7p291gBqr6b http://s7.sinaimg.cn/mw690/007n4XDCzy7p29384Voc6        然后FPGA产生640*480分辨率(使用上表中的第一种分辨率),让显示器产生显示一幅图像。提示:显示器一般都会自适应功能,无须设置就能识别不同分辨率的图像。       图像的内容是:在屏幕的中央显示一个直径为100个像素的圆,圆内的颜色为绿色,圆外的颜色是白色,如下图所示。http://s3.sinaimg.cn/mw690/007n4XDCzy7p2963Gdc02 上板效果图如下图所示(显示器不同,显示效果也会有差别,请注意) 1设计实现1.1 顶层接口      新建目录:D:\mdy_book\vga_exec1。在该目录中,新建一个名为vga_exec1.v的文件,并用GVIM打开,开始编写代码。      我们要实现的功能,概括起来就是FPGA产生VGA时序,即控制VGA_R4~R0、VGA_G5~G0、VGA_B4~B0、VGA_HSYNC和VGA_VSYNC,让显示器显示红色。其中,VGA_HSYNC和VGA_VSYNC,FPGA可根据时序产生高低电平。而颜色数据,由于是固定的红色,FPGA也能自己产生,不需要外部输入图像的数据。那么我们的FPGA工程,可以定义输出信号hys表示行同步,用输出信号vys表示场同步,定义一个16位的信号lcd_rgb,其中lcd_rgb[15:11]表示VGA_R4~0,、lcd_rgb[10:5]表示VGA_G5~0,、lcd_rgb[4:0]表示VGA_B4~0。      我们还需要时钟信号和复位信号来进行工程控制。      综上所述,我们这个工程需要五个信号,时钟clk,复位rst_n,场同步信号vys、行同步信号hys和RGB输出信号lcd_rgb。http://s10.sinaimg.cn/mw690/007n4XDCzy7p29JoxeN09      将module的名称定义为vga_exec1。并且我们已经知道该模块有五个信号:clk、rst_n、lcd_hs、lcd_vs和lcd_rgb。为此,代码如下: http://s14.sinaimg.cn/mw690/007n4XDCzy7p29O7CrP1d       其中clk、rst_n是输入信号,lcd_hs、lcd_vs和lcd_rgb是输出信号,其中clk、rst_n、lcd_hs、lcd_vs的值是0或者1,一根线即可,lcd_rgb为16位位宽的,根据这些信息,我们补充输入输出端口定义。代码如下: http://s1.sinaimg.cn/mw690/007n4XDCzy7p2a0pAkw80       需要注意的是,输入进来的时钟clk是50MHz,而从分辨率参数表可知道,行单位的基准时钟是25MHz。为此我们需要根据50MHz来产生一个25 MHz的时钟,然后再用于产生VGA时序。      为了得到这个25M时钟,我们需要一个PLL。PLL可以认为是FPGA内的一个硬核,它的功能是根据输入的时钟,产生一个或多个倍频和分频后的输出时钟,同时可以调整这些输出时钟的相位、占空比等。      例如,输入进来是50M时钟,如果我需要一个100M时钟,那么从逻辑上、代码上是不可能产生的,我们就必须用到PLL来产生了。     整个工程的结构图如下。http://s14.sinaimg.cn/mw690/007n4XDCzy7p2a3LKAB0d      PLL的生成方式过程,请看本案例的综合工程和上板一节的内容。 3.3    VGA 驱动模块设计3.3.1  接口信号     在目录:D:\mdy_book\vga_exec1中,建立一个vga_driver.v文件,并用GVIM打开,开始编写代码。     我们先分析功能。要控制显示器,让其产生红色,也就是让FPGA控制VGA_R0~4、VGA_G0~5、VGA_B0~4、VGA_VSYNC和VGA_HSYNC信号。那么VGA驱动模块,可以定义输出信号hys表示行同步,用输出信号vys表示场同步,定义一个16位的信号lcd_rgb,其中lcd_rgb[15:11]表示VGA_R4~0,、lcd_rgb[10:5]表示VGA_G5~0,、lcd_rgb[4:0]表示VGA_B4~0。     同时该模块的工作时钟为25M,同时需要一个复位信号。     综上所述,我们这个模块需要五个信号,25M时钟clk,复位rst_n,场同步信号vys、行同步信号hys和RGB输出信号lcd_rgb。     将module的名称定义为vga_driver。并且我们已经知道该模块有五个信号:clk、rst_n、hys、vys和lcd_rgb。为此,代码如下:http://s9.sinaimg.cn/mw690/007n4XDCzy7p2aejq6Q18      其中clk、rst_n是输入信号,hys、vys和lcd_rgb是输出信号,其中clk、rst_n、hys、vys的值是0或者1,一根线即可,lcd_rgb为16位位宽的,根据这些信息,我们补充输入输出端口定义。代码如下: http://s16.sinaimg.cn/mw690/007n4XDCzy7p2aeMAQT7f 3.3.2  信号设计     我们先设计场同步信号hys,VGA时序中的场同步信号,其时序图如下:http://s13.sinaimg.cn/mw690/007n4XDCzy7p2b0UZ88cc      hys就是一个周期性地高低变化的脉冲。我们使用的是下表中的第一种分辨率,也就是同步脉冲a的时间是96个时钟周期,而显示后沿b是48个时钟周期,显示时序c是640个时钟周期,显示前沿是16个时钟周期,一共是800个时钟周期。 http://s13.sinaimg.cn/mw690/007n4XDCzy7p2byvH8U9c      将时间信号填入图中,更新后的时序图如下: http://s10.sinaimg.cn/mw690/007n4XDCzy7p2bNilkZ89       很显然,我们需要1个计数器来产生这个时序,我们将该计数器命名为h_cnt。由于hys是不停地产生的,那么h_cnt就是不停地计数,每个时钟都要计数器,所以认为该计数器的加1条件为“1”,可写成:assign add_h_cnt = 1。从上图可知,该计数器的周期是800。综上所述,该计数器的代码如下:     http://s10.sinaimg.cn/mw690/007n4XDCzy7p2cZmXQ529      有了计数器h_cnt,那么hys信号就有了对齐的对象。从时序图可以发现, hys有两个变化点,一个是h_cnt数到96个时,由0变1;另一个是当h_cnt数到800个时,由1变0。所以,场同步信号的代码如下:http://s4.sinaimg.cn/mw690/007n4XDCzy7p2d0bnNh53       接下来设计vys信号。该信号的时序图如下所示。http://s11.sinaimg.cn/mw690/007n4XDCzy7p2d1Ym8O0a       vys就是一个周期性地高低变化的脉冲。我们使用的是表中的第一种分辨率,查询表可知,同步脉冲a的时间是2行的时间,而显示后沿b是33行,显示时序c是480行,显示前沿是10行,一共是525行。其中,一“行”结束,也就是h_cnt数完了。     将时间信号填入图中,更新后的时序图如下:http://s6.sinaimg.cn/mw690/007n4XDCzy7p2d6mYw5c5       有了计数器v_cnt,那么vys信号就有了对齐的对象。从时序图可以发现, vys有两个变化点,一个是v_cnt数到2个时,由0变1;另一个是当h_cnt数到525个时,由1变0。所以,场同步信号的代码如下:http://s2.sinaimg.cn/mw690/007n4XDCzy7p2d7dWM1f1       最后我们还有一个信号需要设计,那就是lcd_rgb信号。http://s8.sinaimg.cn/mw690/007n4XDCzy7p2d8MM1F87       我们在显示器中一共要显示两种颜色:绿色和白色。lcd_rgb等于16’b00000_111111_00000时表示绿色;lcd_rgb等于16’b11111_111111_11111时表示白色。还要注意的是,在非显示区域,lcd_rgb的值要为0,才能正确显示。我们现在要仔细区分,在什么时候分别输出上面的值。      图像分成两部分:圆内和圆外。圆外显示白色,圆内显示绿色。所以我们进一步分析,如何知道是圆内还是圆外呢?我们需要用到圆的定义公式:(x-a)2+(y-b)2=r2。自然地,当(x-a)2+(y-b)22的区域是圆内,(x-a)2+(y-b)2>=r2的区域就是圆外。     圆的原理和公式是初中的知识,非常简单,我们可以认为这个公式就是一个算法,关键的是我们如何使用FPGA来实现这个算法。常听到有人说,学FPGA工具是很简单的,算法才是最重要的。对于这一点,我不敢苟同。     有读者一听到算法,就觉得很高大上。其实算法就是解决某个问题的数学公式,简单的就是一个加法运算,例如求和、求平均数等,再复杂一些就是FFT等。要创造、发明、改进一套算法很难,这些真是需要一些数学功底很好的高材生才能做的事,需要天赋,训练也训练不出来。我们更多地找到算法、读懂算法、实现算法、解决问题。我们学习FPGA,学习怎么把各种算法用FPGA实现,这是比较实现可行的目标(当然,这是笔者基于自身水平而自定的目标哈,天外有天,人外有人,厉害的人物要忽略这建议)。     现在我们要用FPGA实现算法(x-a)2+(y-b)22,我们应该如何考虑呢?我们重要的是先搞清楚公式里各个元素是什么,在FPGA里面怎么表示。http://s8.sinaimg.cn/mw690/007n4XDCzy7p2hQBQlpf7       首先看r。r是圆的半径,根据功能要求,我们显示的圆的直径是100个像素,所以r的值应该为50。      其次看a和b。a和b是圆心的坐标。我们要在屏幕的中心显示一个圆,中间位置就是行列的中间值,所以a的值为640/2=320,b的值为480/2=240。      最后我们再来看x和y。x和y是现时屏幕上显示的坐标。在屏幕中x的范围是0~639,y的范围是0~479,最左上角的值为(0,0),最右下角的坐标是(639,479)。现在FPGA的信号中没有x和y,但有相似的信号h_cnt和v_cnt,我们想办法将h_cnt和v_cnt表示x和y。      考虑到VGA时序中的同步脉冲和显示前沿的因素,当h_cnt=96+48,v_cnt=2+33时,才表示x=0和y=0。所以用h_cnt和v_cnt来表示x和y,则有x = h_cnt-96-48,y=v_cnt-2-33。      用distance表示距离的平方,则有distance = (x-a)*(x-a) + (y-b)*(y-b) = (h_cnt-96-48 -320) *(h_cnt-96-48-320) +(v_cnt-2-33 -240) *(v_cnt-2-33 -240)http://s11.sinaimg.cn/mw690/007n4XDCzy7p2hTjlRofa ​      如果distance小于r2=(50)2=2500 ,说明在圆内,否则在圆外。      显示区域:(h_cnt >=(96+48)&& h_cnt <</span>(96+48+640)),并且(v_cnt>=(2+33) && v_cnt<</span>(2+33+480))     绿色区域:distance < 2500      白色区域:在显示区域中,非绿色区域的,就是白色区域。非显示区域:显示区域之外的,就是非显示区域。      我们可以设计几个信号来表示这些区域。显示区域用valid_area=1表示,红色区域用red_area=1表示,绿色区域用green_area=1表示。可得到代码如下: http://s15.sinaimg.cn/mw690/007n4XDCzy7p2hXfcMmfe      有了green_area和valid_area后,设计lcd_rgb就好办了。     非显示区域(valid_area=0),lcd_rgb输出“16’b0”;       显示区域(valid_area)中的绿色区域(green_area=1),lcd_rgb输出“16’b00000_111111_00000”;       显示区域(valid_area)中的非绿色区域(green_area=0),lcd_rgb输出“16’b11111_111111_11111”。      则可以写出代码如下:http://s12.sinaimg.cn/mw690/007n4XDCzy7p2hYdZRp1b 3.3.3  信号定义      接下来定义信号类型。     h_cnt是用always产生的信号,因此类型为reg。h_cnt计数的最大值为800,需要用10根线表示,即位宽是10位。因此代码如下: http://s5.sinaimg.cn/mw690/007n4XDCzy7p2i2GoOo24       add_h_cnt和end_h_cnt都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:http://s4.sinaimg.cn/mw690/007n4XDCzy7p2i3VNVVf3 ​      v_cnt是用always产生的信号,因此类型为reg。v_cnt计数的最大值为525,需要用10根线表示,即位宽是10位。因此代码如下:http://s6.sinaimg.cn/mw690/007n4XDCzy7p2i5Kpc9f5 ​     add_v_cnt和end_v_cnt都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下: http://s6.sinaimg.cn/mw690/007n4XDCzy7p2i9PPORd5       lcd_rgb是用always方式设计的,因此类型为reg。并且它的位宽是16位,16根线表示即可。因此代码如下:http://s11.sinaimg.cn/mw690/007n4XDCzy7p2iasHx02a       hys和vys是用always方式设计的,因此类型为reg。并且其值是0或1,需要1根线表示即可。因此代码如下:http://s14.sinaimg.cn/mw690/007n4XDCzy7p2ic9CG1fd ​     distance是用always方式设计的,因此类型为reg。其位宽为20位,需要20根线表示。因此代码如下:http://s7.sinaimg.cn/mw690/007n4XDCzy7p2icT3i636 ​      valid_area和green_area是用always方式设计的,因此类型为reg。并且其值是0或1,用一根线表示即可。因此代码如下:http://s5.sinaimg.cn/mw690/007n4XDCzy7p2ie8xKI34 3.4    顶层模块设计3.4.1  例化子模块     例化PLL IP核的代码http://s11.sinaimg.cn/mw690/007n4XDCzy7p2ihWpke4a ​     例化驱动模块的代码http://s2.sinaimg.cn/mw690/007n4XDCzy7p2iiF5oRb1 3.4.2    信号定义       clk_0是在例化文件中,因此类型为wire。并且其值是0或1,用一根线表示即可。因此代码如下: http://s1.sinaimg.cn/mw690/007n4XDCzy7p2insOQwd0       lcd_sh和lcd_vs是在例化文件中,因此类型为wire。并且其值是0或1,用一根线表示即可。因此代码如下:http://s3.sinaimg.cn/mw690/007n4XDCzy7p2iosJ4S42 ​      lcd_rgb是在例化文件中,因此类型为wire。它的位宽是16位的,用16根线表示即可。因此代码如下: http://s5.sinaimg.cn/mw690/007n4XDCzy7p2ip9Sa854 4     综合与上板4.1  新建工程     首先在d盘中创建名为“vga_exec1”的工程文件夹,将写的代码命名为“vga_exec1.v”,顶层模块名为“vga_exec1”,例化文件命名为“vga_driver.v”。http://s11.sinaimg.cn/mw690/007n4XDCzy7p2ixGZzY2a http://s7.sinaimg.cn/mw690/007n4XDCzy7p2iyxiR0f6 http://s10.sinaimg.cn/mw690/007n4XDCzy7p2iyLFcR29      然后打开Quartus Ⅱ,点击File下拉列表中的New Project Wzard...新建工程选项。http://s6.sinaimg.cn/mw690/007n4XDCzy7p2izWHad45 ​      3.在出现的界面中直接点击最下方的“Next”。​ http://s7.sinaimg.cn/mw690/007n4XDCzy7p2iANdGuf6       4.之后出现的是工程文件夹、工程名、顶层模块名设置界面。按照之前的命名进行填写,第一栏选择工程文件夹“vga_exec1”,第二栏选择工程文件“vga_exec1.v”,最后一栏选择顶层模块名“vga_exec1”,然后点击”Next”,在出现的界面选择emptyproject。http://s15.sinaimg.cn/mw690/007n4XDCzy7p2iEylMq3e http://s16.sinaimg.cn/mw690/007n4XDCzy7p2iF3u91df ​      5.之后是文件添加界面。在上方一栏中添加之前写的”vga_driver.v和vga_exec1.v”文件和生成的“my_pll.v”,点击右侧的“Add”按钮,之后文件还会出现在大方框中,之后点击“Next”。http://s1.sinaimg.cn/mw690/007n4XDCzy7p2iGiI1id0 ​        器件型号选择界面。在“Device family”处选择Cyclone ⅣE,在“Available devices”处选择EP4CE15F23C8,然后点击“Next”。http://s8.sinaimg.cn/mw690/007n4XDCzy7p2iHB4Zp37 ​     EDA工具界面。该页面用默认的就行,直接点击最下方“Next”。http://s1.sinaimg.cn/mw690/007n4XDCzy7p2iIKxiM20 ​      8.之后出现的界面是我们前面的设置的总结,确认没有错误后点击“Finish”。 http://s15.sinaimg.cn/mw690/007n4XDCzy7p2iKFiAm6e 4.2   生成PLL IP核      新建工程后,就要生成PLL IP核。本节的PLL生成过程,与案例“VGA显示颜色”第四点综合工程和上板中的PLL内容一致,注意其中的地址有不同。 4.3   综合      1.新建工程步骤完成后,就会出现以下界面。在“Project Navigator”下选中要编译的文件,点击上方工具栏中“StartCompilation”编译按钮(蓝色三角形)。http://s6.sinaimg.cn/mw690/007n4XDCzy7p2iOqkjb05       2.编译成功后会出现以下界面,点击“OK”。http://s5.sinaimg.cn/mw690/007n4XDCzy7p2iPqOFuf4 4.4    配置管脚http://s14.sinaimg.cn/mw690/007n4XDCzy7p2iR3lNj3d ​      在菜单栏中,选中Assignments,然后选择Pin Planner,就会弹出配置管脚的窗口。http://s15.sinaimg.cn/mw690/007n4XDCzy7p2iS8o587e 在配置窗口最下方中的location一列,参考下表中最右两列配置好FPGA管脚。 http://s6.sinaimg.cn/mw690/007n4XDCzy7p2iTUL0F55      配置完成后,关闭Pin Planner,软件自动会保存管脚配置信息。4.5   再次综合http://s2.sinaimg.cn/mw690/007n4XDCzy7p2iZUygV71        在菜单栏中,选中Processing,然后选择Start Compilation,再次对整个工程进行编译和综合。http://s11.sinaimg.cn/mw690/007n4XDCzy7p2j1Dygaea       出现上面的界面,就说明编译综合成功。4.6    连接开发板      图中,下载器接入电脑USB接口,电源接入电源,vga线连接显示器,然后摁下电源开关,看到开发板灯亮。http://s5.sinaimg.cn/mw690/007n4XDCzy7p2j3Q64Q74 ​4.7     上板     1.双击Tasks一栏中”Program Device”。 http://s5.sinaimg.cn/mw690/007n4XDCzy7p2j6dWHG14       2.会出现如下界面,点击add file添加.sof文件,在右侧点击“Start”,会在上方的“Progress”处显示进度。http://s5.sinaimg.cn/mw690/007n4XDCzy7p2j8WxCI34 ​3.进度条中提示成功后,即可在显示器上观察到相应的现象。 此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2018-11-29
  • 发表了主题帖: 信号发生器和DA转换-明德扬至简设计与应用FPGA

      1     项目背景    (技术交流群544453837) 1.1   信号发生器        信号发生器又称信号源或振荡器,是一种能提供各种频率、波形和输出电平电信号的设备,在测量各种电信系统或电信设备的振幅特性、频率特性、传输特性及其它电参数时,以及测量元器件的特性与参数时,用作测试的信号源或激励源,在生产实践和科技领域中有着广泛的应用。     直接数字式频率合成器(DDS)是将先进的数字处理理论与方法引入频率合成的一项新技术,它把一系列数字量形式的信号通过数/模转换器转换成模拟量形式的信号。http://s15.sinaimg.cn/mw690/007n4XDCzy7p2uBHG1M4e       上图是一个典型的DDS工程。DDS一般可分为相位累加器、信号转换器和DAC。      DDS的输入是频率控制字,它用来控制相位累加器每次增加的相位值,相当于一个步进值。      相位累加器:每来一个时钟脉冲,就会在原来相位值的基础上,加上频率控制字的值,得到最新的相位值,将相位值将输出给信号转换器。      信号转换器:一般转换器内部有一片ROM,事先保存了要产生波形的幅度值。根据输入的的相位值,就能输出该相位值所对应的信号幅度值。例如将一个完整周期的正弦波等距离分成128份,并保存到转换器的ROM当中。当相位值为0时,就输出相位为0所应对的幅度值;当相位为100时,就输出相位为100所对应的幅度值。       的具体工作过程是由N位相位累加器、N位加法器和N位累加寄存器组成。每来一个时钟脉冲,N位加法器将频率控制字K与N位累加寄存器输出的累加相位数据相加,并把相加后的结果送至累加寄存器的输入端。累加寄存器一方面将上一时钟周期作用后所产生的新的相位数据反馈到加法器的输入端,使加法器在下一时钟的作用下继续与频率控制字K相加;另一方面将这个值作为取样地址送入幅度/相位转换电路,幅度/相位转换电路根据这个地址输出相应的波形数据。最后经D/A转换器和 LPF将波形数据转换成所需要的模拟波形。 1.2 DA转换         明德扬教学板板载双通道、125MHz 转换速率、8bi的高速DA芯片,满足常用信号发生器、滤波信号输出等需求。实际位置如下所示:http://s4.sinaimg.cn/mw690/007n4XDCzy7p2uFxC2T13       芯片型号是AD9709,AD9709是一款双端口、高速、双通道、8位CMOS DAC,其中集成两个高品质8位TxDAC+®内核、一个基准电压源和数字接口电路,采用48引脚小型LQFP封装。它提供出色的交流和直流性能,同时支持最高125 MSPS的更新速率。http://s14.sinaimg.cn/mw690/007n4XDCzy7p2uHRZxj4d http://s10.sinaimg.cn/mw690/007n4XDCzy7p2uIIUJz89       与FPGA相连的信号有:DA_CLKA、DA_CLKB、DAC_DB7~0、DAC_DA7~0,DAC_MODE、DAC_SLEEP、DA_WRA和DA_WRB。 http://s1.sinaimg.cn/mw690/007n4XDCzy7p2uL7fgY50 1.3    AD9709的时序      AD9709的控制时序如下图http://s15.sinaimg.cn/mw690/007n4XDCzy7p2uNaksS4e      在双通道模式中,通道A和通道B就如两个独立的DA芯片。其中DA_CLKA、DAC_DA7~0、DAC_WR_A用于控制通道A,DA_CLKB、DA_DB7~0、DA_WRB用于控制通道B。      以控制通道A为例,时序图要求,要先将数据输出到DAC_DA7~0,然后经过ts时间后,将DAC_WRA和DA_CLKA变高,此时DAC就将数据锁住,经过一段时间后,就会输出数据所对应的电流,经过电路转换后就变成对应电压了。      时序图中要注意几点(数据手册有详细说明)     1. DA_CLKA并且超前于或者同时与DA_WRA由0变1。     2. 图中tS(DAC_WRA上升沿前数据保持不变的时间)、tH(DAC_WRA上升沿后数据保值不变的时间)、tLPW(DAC_WRA的高电平时间)、tCPW(DAC_CLKA的高电平时间)等参数,查询数据手册,可以得到如下参数表。从表中可以看到tS的时间至少是2ns;tH时间至少是1.5ns;tLPW、tCPW时间至少是3.5ns。图中规定了至少,只要大于要求都是可以的。http://s13.sinaimg.cn/mw690/007n4XDCzy7p2uTdgyEec       通道B的时序要求和通道A是相同的,仅是控制信号不同。      明德扬教学板的AD9709的两个通道,均支持0.48~2.2V的电压输出,这个输出电压与输入数据的关系,可用下面的公式表示:      通道A的输出电压 = -1.72 * (DAC_DA /255) + 2.2 V     通道B的输出电压 = -1.72 * (DAC_DB/255) + 2.2 Vhttp://s10.sinaimg.cn/mw690/007n4XDCzy7p2uVGDix99       由公式可见,输出电压与DAC_DA/B的值是成线性反比例关系,最低电压为0.48V,最高为2.2V。需要指出的是,由于电路原理图的原因才导致电压在此范围,不同电路实现是不相同的。2   设计目标      本次案例将使用到采样率大于100M的示波器。将示波器和教学板上的通道1连接,如下图所http://s11.sinaimg.cn/mw690/007n4XDCzy7p2uZlnSO0a       本案例是要让DA输出不同频率的正弦波。共输出方式如下:      1.       连续输出2个周期为6.25MHz的正弦波,其中每个正弦波输出16个采样点;      2.       连续输出2个周期为3.125MHz的正弦波,其中每个正弦波输出32个采样点;      3.      连续输出2个周期为1.5625MHz的正弦波,其中每个正弦波输出128个采样点;      4.       连续输出2个周期为781250Hz的正弦波,其中每个正弦波输出128个采样点;      5.       连续输出2个周期为390625Hz的正弦波,其中每个正弦波输出128个采样点;      6.      连续输出2个周期为195312.5Hz的正弦波,其中每个正弦波输出128个采样点。     重复以上的1~7的步骤。      正弦波的最高电压是2.2V,最低电压是0.48V     示波器的显示结果如下图http://s2.sinaimg.cn/mw690/007n4XDCzy7p2v4qphf21 上图是整体效果图,每种频率的正弦波连续出现2次,并且正弦波的周期越来越大。下图是捕捉到的,频率为6.25MHz的正弦波,最高电压是2.2V,最低电压是0.48V。下图是捕捉到的,频率为3.125MHz的正弦波,最高电压是2.2V,最低电压是0.48V。下图是捕捉到的,频率为1.5625MHz的正弦波,最高电压是2.2V,最低电压是0.48V。下图是捕捉到的,频率为390625Hz的正弦波,最高电压是2.2V,最低电压是0.48V。下图是捕捉到的,频率为195312.5Hz的正弦波,最高电压是2.2V,最低电压是0.48V。3   设计实现3.1 顶层接口       新建目录:D:\mdy_book\dds_da。在该目录中,新建一个名为dds_da.v的文件,并用GVIM打开,开始编写代码。       我们要实现的功能,概括起来就是FPGA产生控制AD9709,让其中的通道A产生正弦波所对应的电压。为了控制AD9709的通道A,就需要控制AD9709的MODE、SLEEP、CLK1、WRT1、DB7~0P1管脚。根据设计目标的要求,整个工程需要以下信号:1.       使用clk连接到晶振,表示50M时钟的输入。2.       使用rst_n连接到按键,表示复位信号。3.       使用dac_mode信号连接到AD9709的MODE管脚,用来控制其工作模式。4.       使用dac_sleep信号连接到AD9709的SLEEP管脚,用来控制其睡眠模式。5.       使用dac_clka信号连接到AD9709的CLK1管脚,用来控制通道A的时钟。6.       使用dac_wra信号连接到AD9709的WRT1管脚,用来控制通道A的写使能。7.       使用8位信号dac_da连接到AD9709的DB7~0P1管脚,用来控制通道A的写数据。     综上所述,我们这个工程需要7个信号,时钟clk,复位rst_n,dac_mode、dac_sleep、dac_clka、dac_wra和dac_da,其中dac_da是8位信号,其他都是1位信号。     下面表格表示了硬件电路图的连接关系。http://s6.sinaimg.cn/mw690/007n4XDCzy7p2v9Gm2N65 将module的名称定义为dds_da,代码如下: http://s10.sinaimg.cn/mw690/007n4XDCzy7p2vd1UV399      其中clk、rst_n是1位的输入信号,dac_da是8位的输出信号,dac_mode,dac_clka,dac_wra,dac_sleep是一位输出信号。 http://s14.sinaimg.cn/mw690/007n4XDCzy7p2ver0ct2d 3.2   信号设计      我们先分析下DAC的输出。以频率为195312.5Hz的正弦波为例,如下图。频率为195312.5Hz,也就是一个正弦波的周期是5120ns。案例要求一个周期要输出128个点,那就是每隔5120/128=40ns要输出一个点。考虑到工程输入的时钟是50MHz,周期是20ns,那就意味着每隔2个时钟就要输出一个点。     综上所述,产生频率频率为195312.5Hz的正弦波,就是每隔2个时钟输出一个电压值,一共输出128个点,组成一个正弦波。我们要连续产生2个正弦波。     现在进一步分析下,这128个点所对应电压值是多少?由于教学板的输出电压在0.48~2.2V之间,最低值是0.48V,最高值是2.2V。      先将一个标准的正弦波向上平稳1个单位,使得范围变成0~2。然后等间隔取128个点(间隔为2*pi/128),获取其幅度值。我们再用8位信号sin_data表示这些幅度值,其表示方式为:     sin_data = (sin(2*pi/128) + 1) * (255/2),i为0~127     (公式1)http://s11.sinaimg.cn/mw690/007n4XDCzy7p2vji4qu3a       通道A的输出电压 = -1.72 * (DAC /255) + 2.2 V      公式中可以看到,通道A的输出电压是与DAC_DA成线性反比例关系。为了让通道A的电压正确地展现出正弦波,我们还需要做如下调整。      DAC_DA = 255 - sin_data      上面的DAC_DA就是最终输出给DA芯片的数据值,即dac_da信号。     综上所述,产生频率为195312.5Hz的正弦波,就是每隔2个时钟输出一个电压值dac_da。先按表XX每隔1个选出sin_data,再用(255-sin_data)得到dac_da。一共输出128个点,组成一个正弦波,并且连续输出2个正弦波。      以相同的分析方法,分析频率为6.25MHz的正弦波。     频率为6.25MHz,也就是一个正弦波的周期是160ns。案例要求一个周期要输出8个点,那就是每隔160/8=20ns要输出一个点。考虑到工程输入的时钟是50MHz,周期是20ns,那就意味着每隔1个时钟就要输出一个点。     先将一个标准的正弦波向上平稳1个单位,使得范围变成0~2。然后等间隔取8个点(间隔为2*pi/8),获取其幅度值。我们再用8位信号sin_data表示这些幅度值,其表示方式为:sin_data = (sin(2*pi/8) + 1) * (255/2),i为0~7            = (sin(2*pi*16/128) + 1) * (255/2),i为0~7   (公式2)     对比公式1和公式2,发现同样可以由表XX得到相应的sin_data,只是此时间隔16个点取一个值,一共取8个。      综上所述,产生频率为6.25MHz的正弦波,就是每隔1个时钟输出一个电压值dac_da,按表XX中每隔16个点输出一个值,再用(255-sin_data)得到dac_da。一共输出8个点,组成一个正弦波,并且连续产生2个正弦波 .     按同样的分析方法,分析其他频率。最终总结如下:   1.       连续输出2个周期为6.25MHz的正弦波,其中每个正弦波输出8个采样点。      等价于:每隔1个时钟输出一个电压值dac_da,一共输出8个点,组成一个正弦波,连续产生2个正弦波。dac_da的产生方式:表XXX每隔16个选出得到sin_data,通过(255-sin_data)得到dac_da。      2.       连续输出2个周期为3.125MHz的正弦波,其中每个正弦波输出16个采样点。等价于:每隔1个时钟输出一个电压值dac_da,一共输出16个,组成一个正弦波,连续产生2个正弦波。dac_da的产生方式:表XXX每隔8个选出得到sin_data,通过(255-sin_data)得到dac_da。      3.       连续输出2个周期为1.5625MHz的正弦波,其中每个正弦波输出32个采样点。等价于:每隔1个时钟输出一个电压值dac_da,一共输出32个,组成一个正弦波,连续产生2个正弦波。     dac_da的产生方式:表XXX每隔4个选出得到sin_data,通过(255-sin_data)得到dac_da。     4.      连续输出2个周期为781250Hz的正弦波,其中每个正弦波输出64个采样点。等价于:每隔1个时钟输出一个电压值dac_da,一共输出64个点,组成一个正弦波,连续产生2个正弦波。dac_da的产生方式:表XXX每隔2个选出得到sin_data,通过(255-sin_data)得到dac_da。5.       连续输出2个周期为390625Hz的正弦波,其中每个正弦波输出128个采样点。等价于:每隔1个时钟输出一个电压值dac_da,一共输出128个点,组成一个正弦波,连续产生2个正弦波。dac_da的产生方式:表XXX每隔1个选出得到sin_data,通过(255-sin_data)得到dac_da。      6.       连续输出2个周期为195312.5Hz的正弦波,其中每个正弦波输出128个采样点。等价于:每隔2个时钟输出一个电压值dac_da,一共输出128个点,组成一个正弦波,连续产生2个正弦波。dac_da的产生方式:表XXX每隔1个选出得到sin_data,通过(255-sin_data)得到dac_da。       按照至简设计法中的变量法思想,那么可以概括上面的功能:每隔x个时钟输出一个电压值,一共输出y个点,组成一个正弦波,每个要产生要连续产生2个正弦波。由于一共要产生6种不同频率的正弦波,所以还需要一个计数器来数6个。      总结出上面的内容后,我们开始设计代码。“每隔x个时钟输出一个电压值”,所以这需要一个计数器cnt0,加1条件是“1”,结束条件是数到x个,可以得到cnt0的代码。http://s10.sinaimg.cn/mw690/007n4XDCzy7p2vtBEcp69      “一共输出y个点”,这同样需要一个计数器cnt1。注意的是,由于每个点维持x个时钟,也就是cnt1的加1条件是“数到x个时钟”,即end_cnt0。结束条件是:数到y下。可以得到cnt1的代码。http://s12.sinaimg.cn/mw690/007n4XDCzy7p2vvV5sn3b       “每个要产生要连续产生2个正弦波”,这也需要一个计数器cnt2。一个正弦波由y个点组成,所以cnt2的加1条件是“数到y个”,即end_cnt1,结束条件是“数到2个”。可以得到cnt2的代码: http://s12.sinaimg.cn/mw690/007n4XDCzy7p2vwJK8r7b        由于一共要产生6种不同频率的正弦波,所以还需要一个计数器cnt3来数6个。这个cnt3的加1条件是“产生完2个正弦波”,即end_cnt2,结束条件是“数到6个”。可以得到cnt3的代码。 http://s1.sinaimg.cn/mw690/007n4XDCzy7p2vz2rW8c0       我们定义了变量x和y,其中x表示相隔的时钟数,y表示一个正弦波的采样点数。具体的x和y是与正弦波的不同频率相关的,也就是与cnt3相关。根据题意和至简设计法中的变量设计方法,可以得到x和y的代码。 http://s8.sinaimg.cn/mw690/007n4XDCzy7p2vC6sMn77       有了计数器之后,其他信号就可以根据计数器设计出来了。      首先看信号dac_da。dac_da都是按(255-sin_data)得到。那么可以写出dac_da的代码http://s7.sinaimg.cn/mw690/007n4XDCzy7p2vErROud6       接下来看sin_data信号。sin_data是从表XX中选择出来的值,不同的频率,选择的方式不同。那么很自然是定义一个选择信号addr。我们只要控制好addr,就能方便得到sin_data。http://s6.sinaimg.cn/mw690/007n4XDCzy7p2vMWGfba5 http://s4.sinaimg.cn/mw690/007n4XDCzy7p2vNlQyfa3 http://s1.sinaimg.cn/mw690/007n4XDCzy7p2vOc2Zyd0 http://s10.sinaimg.cn/mw690/007n4XDCzy7p2vP6p1Tf9 http://s6.sinaimg.cn/mw690/007n4XDCzy7p2vPv8O195 http://s10.sinaimg.cn/mw690/007n4XDCzy7p2vQhkkV39       接下来设计信号addr。addr是用来控制选择数据的地址,不同频率的正弦波要求地址控制方式不同。频率为6.25MHz(cnt3=0)是每隔16个选择一个;频率为3.125MHz(cnt3=1)是每隔8个点选择一个;频率为1.5625MHz(cnt3=2)是每隔4个点选择一个;频率为781250Hz(cnt3=3)是每隔2个选择一个;频率为390625Hz(cnt3=4)是每隔1个点选择一个;频率为195312.5Hz(cnt3=5)是每隔1个选择一个,一共发送128个。     我们用cnt1表示发送的第几个数。     cnt3==0 时,addr = cnt1*16;     cnt3==1时,addr = cnt1*8;     cnt3==2时,addr = cnt1*4;     cnt3==3时,addr = cnt1*2;     cnt3==4时,addr = cnt1*1;     cnt3==5时,addr = cnt1*1。     因此,可以写得addr的代码http://s5.sinaimg.cn/mw690/007n4XDCzy7p2vUxDLea4        接下来是信号dac_sleep,AD是一直工作的,所以要让dac_sleep一直为0。     dac_clka为了满足tS的时间要求,可以让dac_clka = ~clk。      dac_wra可以与dac_clka相同。      dac_mode是控制AD9709的模式,当为高电平时,表示双通道模式,此时通过DA、DB两组信号分别独立控制两个通道。在能实现功能的前提下,越简单越好,就使用双通道模式,因此令dac_mode一直为1。http://s1.sinaimg.cn/mw690/007n4XDCzy7p2vWs5K820 至此,主体程序已经完成。接下来是将module补充完整。 3.3  信号定义     接下来定义信号类型。     cnt0是用always产生的信号,因此类型为reg。cnt0计数的最大值为15,需要用5根线表示,即位宽是5位。add_cnt0和end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:http://s16.sinaimg.cn/mw690/007n4XDCzy7p2w0KVenef      cnt1是用always产生的信号,因此类型为reg。cnt1计数的最大值为127,需要用8根线表示,即位宽是8位。add_cnt1和end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下: http://s12.sinaimg.cn/mw690/007n4XDCzy7p2w1KqP96b      cnt2是用always产生的信号,因此类型为reg。cnt2计数的最大值为7,需要用3根线表示,即位宽是8位。add_cnt2和end_cnt2都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下:http://s3.sinaimg.cn/mw690/007n4XDCzy7p2w3rqCu12       cnt3是用always产生的信号,因此类型为reg。cnt3计数的最大值为5,需要用3根线表示,即位宽是3位。add_cnt3和end_cnt3都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下:http://s2.sinaimg.cn/mw690/007n4XDCzy7p2w4Qqrvb1      变量x,y是用always方式设计的,因此类型为reg,x最大值为2,要有2位来表示,y最大值为128,要有8根线表示,即位宽为8,因此代码如下, http://s12.sinaimg.cn/mw690/007n4XDCzy7p2w5Id2bab       addr是用always设计的,因此类型为reg。其值最大为127,一共有7根线,位宽为7,故而代码如下 http://s7.sinaimg.cn/mw690/007n4XDCzy7p2w7XWuid6      sin_data是用always设计的,因此类型为reg。其最大值为255,要有8根线表示,位宽为8,故而代码如下:http://s1.sinaimg.cn/mw690/007n4XDCzy7p2wcfM0o20       dac_da是用always设计的,因此类型为reg。其位宽为8;dac_sleep是用assign设计的,因此类型为wire,位宽为1;dac_wra是用assign设计的,因此类型为wire,位宽为1;dac_clka是用assign设计的,因此类型为wire,位宽为1;dac_mode是用assign设计的,因此类型为wire,位宽为1。故而代码如下:http://s7.sinaimg.cn/mw690/007n4XDCzy7p2wcZHbU66        在代码的最后一行写下endmodule.http://s16.sinaimg.cn/mw690/007n4XDCzy7p2wdMx0bdf 4    综合工程和上板4.1 新建工程http://s5.sinaimg.cn/mw690/007n4XDCzy7p2wiR1Yw24    1.)打开quartus,点击File 在File菜单中选择New ProjectWizard.... 。 http://s11.sinaimg.cn/mw690/007n4XDCzy7p2wMB3jIaa        2.弹出Introduction界面选择Next。http://s2.sinaimg.cn/mw690/007n4XDCzy7p2wNOu3Le1       (3)设置工程目录,工程名,顶层模块名      工程目录设置为:D:\mdy_book\dds_da      工程名:dds_da      顶层模块名:dds_da     填写完毕后,点击next之后进入下一界面。http://s13.sinaimg.cn/mw690/007n4XDCzy7p2wQzEdefc      工程类型界面,Project Type选择Empty project,选择空白工程。点Next进入下一个界面。http://s8.sinaimg.cn/mw690/007n4XDCzy7p2wSx7Ib27        (3.)在文件添加界面,点击右上角的,在弹出来的窗口中,双击选择D:\mdy_book\dds_da目录下的dds_da.v文件。 http://s12.sinaimg.cn/mw690/007n4XDCzy7p2wUeDs77b       点击右上角的add按键,将文件添加进工程。http://s14.sinaimg.cn/mw690/007n4XDCzy7p2wVguZv6d       在主窗口中会显示将dds_da.v加入了工程。点击Next,进入下一个界面。 http://s8.sinaimg.cn/mw690/007n4XDCzy7p2wWFtSD07 4.2   综合 http://s9.sinaimg.cn/mw690/007n4XDCzy7p2x0slAc18       在菜单栏中,选中Processing,然后选择Start Compilation,开始对整个工程进行编译和综合。http://s11.sinaimg.cn/mw690/007n4XDCzy7p2x0M3zA0a       出现上面的界面,就说明编译综合成功。 http://s8.sinaimg.cn/mw690/007n4XDCzy7p2x3pANNe7 4.3 配置管脚 http://s3.sinaimg.cn/mw690/007n4XDCzy7p2x6MoH892       在菜单栏中,选中Assignments,然后选择Pin Planner,就会弹出配置管脚的窗口。 http://s16.sinaimg.cn/mw690/007n4XDCzy7p2x7JUhF0f       在配置窗口中的location一列,可以填写每个管脚所对应的FPGA管脚号。      按上面配置好每个信号的管脚,其最终效果如下图。4.4 再次综合http://s6.sinaimg.cn/mw690/007n4XDCzy7p2xsQWd715     在菜单栏中,选中Processing,然后选择Start Compilation,开始对整个工程进行编译和综合。http://s13.sinaimg.cn/mw690/007n4XDCzy7p2xwUpMo3c     出现上面的界面,就说明编译综合成功. 4.5   连接开发板 http://s14.sinaimg.cn/mw690/007n4XDCzy7p2xGmU0tcd       连接示意如上图所示。将电源接上开发板;USBBLASTER一端连接到JTAG插口,另一端连到PC的USB接口;将开发板上的P7接口与示波器相连。最后再将电源打开。 4.6  上板 http://s10.sinaimg.cn/mw690/007n4XDCzy7p2xKuGWl99       在quartus的Task窗口中,右键Program Device 选择Open 进入烧录界面。http://s1.sinaimg.cn/mw690/007n4XDCzy7p2xLa4lqc0 在上面的界面中,默认会选中文件output/dds_da.sof,如果没有生成请看XXXX。http://s15.sinaimg.cn/mw690/007n4XDCzy7p2xNFodw4e 点击statr,在progress这一条显示100%即表示成功,此时可以看FPGA输出效果了。 此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2018-11-22
  • 发表了主题帖: 2019FPGA就业竞争与指导

    2019FPGA就业竞争与指导 此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2018-11-20
  • 回复了主题帖: 抢先学即将出版的FPGA设计系列书籍免费下载

    源码下载,文档视频教程 请到我们交流群544453837下载

  • 发表了主题帖: 抢先学即将出版的FPGA设计系列书籍免费下载

    下载文档提供两本书籍,其中一本书籍已在北航出版,抢先学习另一本准备出版的新书,大家可免费下载学习。 http://www.openedv.com/data/attachment/forum/201811/19/233500g2ocppmykz5ss16f.jpg http://www.openedv.com/data/attachment/forum/201811/19/233508gtoj60gv4505l5tz.jpg 此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处 http://www.openedv.com/data/attachment/forum/201811/19/233514o3f64fffl3fv111s.jpghttp://www.openedv.com/data/attachment/forum/201811/19/232530vtegvw00bkhg1v2w.png

  • 2018-11-16
  • 发表了主题帖: 信号发生器和DA转换 FPGA案例教程

    1     项目背景   源码下载技术交流群:544453837 暗号:fpga1.1   信号发生器      信号发生器又称信号源或振荡器,是一种能提供各种频率、波形和输出电平电信号的设备,在测量各种电信系统或电信设备的振幅特性、频率特性、传输特性及其它电参数时,以及测量元器件的特性与参数时,用作测试的信号源或激励源,在生产实践和科技领域中有着广泛的应用。     直接数字式频率合成器(DDS)是将先进的数字处理理论与方法引入频率合成的一项新技术,它把一系列数字量形式的信号通过数/模转换器转换成模拟量形式的信号。http://s15.sinaimg.cn/mw690/007n4XDCzy7p2uBHG1M4e       上图是一个典型的DDS工程。DDS一般可分为相位累加器、信号转换器和DAC。      DDS的输入是频率控制字,它用来控制相位累加器每次增加的相位值,相当于一个步进值。      相位累加器:每来一个时钟脉冲,就会在原来相位值的基础上,加上频率控制字的值,得到最新的相位值,将相位值将输出给信号转换器。      信号转换器:一般转换器内部有一片ROM,事先保存了要产生波形的幅度值。根据输入的的相位值,就能输出该相位值所对应的信号幅度值。例如将一个完整周期的正弦波等距离分成128份,并保存到转换器的ROM当中。当相位值为0时,就输出相位为0所应对的幅度值;当相位为100时,就输出相位为100所对应的幅度值。       的具体工作过程是由N位相位累加器、N位加法器和N位累加寄存器组成。每来一个时钟脉冲,N位加法器将频率控制字K与N位累加寄存器输出的累加相位数据相加,并把相加后的结果送至累加寄存器的输入端。累加寄存器一方面将上一时钟周期作用后所产生的新的相位数据反馈到加法器的输入端,使加法器在下一时钟的作用下继续与频率控制字K相加;另一方面将这个值作为取样地址送入幅度/相位转换电路,幅度/相位转换电路根据这个地址输出相应的波形数据。最后经D/A转换器和 LPF将波形数据转换成所需要的模拟波形。 1.2 DA转换         明德扬教学板板载双通道、125MHz 转换速率、8bi的高速DA芯片,满足常用信号发生器、滤波信号输出等需求。实际位置如下所示:http://s4.sinaimg.cn/mw690/007n4XDCzy7p2uFxC2T13       芯片型号是AD9709,AD9709是一款双端口、高速、双通道、8位CMOS DAC,其中集成两个高品质8位TxDAC+®内核、一个基准电压源和数字接口电路,采用48引脚小型LQFP封装。它提供出色的交流和直流性能,同时支持最高125 MSPS的更新速率。http://s14.sinaimg.cn/mw690/007n4XDCzy7p2uHRZxj4d http://s10.sinaimg.cn/mw690/007n4XDCzy7p2uIIUJz89       与FPGA相连的信号有:DA_CLKA、DA_CLKB、DAC_DB7~0、DAC_DA7~0,DAC_MODE、DAC_SLEEP、DA_WRA和DA_WRB。 http://s1.sinaimg.cn/mw690/007n4XDCzy7p2uL7fgY50 1.3    AD9709的时序      AD9709的控制时序如下图http://s15.sinaimg.cn/mw690/007n4XDCzy7p2uNaksS4e      在双通道模式中,通道A和通道B就如两个独立的DA芯片。其中DA_CLKA、DAC_DA7~0、DAC_WR_A用于控制通道A,DA_CLKB、DA_DB7~0、DA_WRB用于控制通道B。      以控制通道A为例,时序图要求,要先将数据输出到DAC_DA7~0,然后经过ts时间后,将DAC_WRA和DA_CLKA变高,此时DAC就将数据锁住,经过一段时间后,就会输出数据所对应的电流,经过电路转换后就变成对应电压了。      时序图中要注意几点(数据手册有详细说明)     1. DA_CLKA并且超前于或者同时与DA_WRA由0变1。     2. 图中tS(DAC_WRA上升沿前数据保持不变的时间)、tH(DAC_WRA上升沿后数据保值不变的时间)、tLPW(DAC_WRA的高电平时间)、tCPW(DAC_CLKA的高电平时间)等参数,查询数据手册,可以得到如下参数表。从表中可以看到tS的时间至少是2ns;tH时间至少是1.5ns;tLPW、tCPW时间至少是3.5ns。图中规定了至少,只要大于要求都是可以的。http://s13.sinaimg.cn/mw690/007n4XDCzy7p2uTdgyEec       通道B的时序要求和通道A是相同的,仅是控制信号不同。      明德扬教学板的AD9709的两个通道,均支持0.48~2.2V的电压输出,这个输出电压与输入数据的关系,可用下面的公式表示:      通道A的输出电压 = -1.72 * (DAC_DA /255) + 2.2 V     通道B的输出电压 = -1.72 * (DAC_DB/255) + 2.2 Vhttp://s10.sinaimg.cn/mw690/007n4XDCzy7p2uVGDix99       由公式可见,输出电压与DAC_DA/B的值是成线性反比例关系,最低电压为0.48V,最高为2.2V。需要指出的是,由于电路原理图的原因才导致电压在此范围,不同电路实现是不相同的。2   设计目标      本次案例将使用到采样率大于100M的示波器。将示波器和教学板上的通道1连接,如下图所http://s11.sinaimg.cn/mw690/007n4XDCzy7p2uZlnSO0a       本案例是要让DA输出不同频率的正弦波。共输出方式如下:      1.       连续输出2个周期为6.25MHz的正弦波,其中每个正弦波输出16个采样点;      2.       连续输出2个周期为3.125MHz的正弦波,其中每个正弦波输出32个采样点;      3.      连续输出2个周期为1.5625MHz的正弦波,其中每个正弦波输出128个采样点;      4.       连续输出2个周期为781250Hz的正弦波,其中每个正弦波输出128个采样点;      5.       连续输出2个周期为390625Hz的正弦波,其中每个正弦波输出128个采样点;      6.      连续输出2个周期为195312.5Hz的正弦波,其中每个正弦波输出128个采样点。     重复以上的1~7的步骤。      正弦波的最高电压是2.2V,最低电压是0.48V     示波器的显示结果如下图http://s2.sinaimg.cn/mw690/007n4XDCzy7p2v4qphf21 上图是整体效果图,每种频率的正弦波连续出现2次,并且正弦波的周期越来越大。下图是捕捉到的,频率为6.25MHz的正弦波,最高电压是2.2V,最低电压是0.48V。下图是捕捉到的,频率为3.125MHz的正弦波,最高电压是2.2V,最低电压是0.48V。下图是捕捉到的,频率为1.5625MHz的正弦波,最高电压是2.2V,最低电压是0.48V。下图是捕捉到的,频率为390625Hz的正弦波,最高电压是2.2V,最低电压是0.48V。下图是捕捉到的,频率为195312.5Hz的正弦波,最高电压是2.2V,最低电压是0.48V。3   设计实现3.1 顶层接口       新建目录:D:\mdy_book\dds_da。在该目录中,新建一个名为dds_da.v的文件,并用GVIM打开,开始编写代码。       我们要实现的功能,概括起来就是FPGA产生控制AD9709,让其中的通道A产生正弦波所对应的电压。为了控制AD9709的通道A,就需要控制AD9709的MODE、SLEEP、CLK1、WRT1、DB7~0P1管脚。根据设计目标的要求,整个工程需要以下信号:1.       使用clk连接到晶振,表示50M时钟的输入。2.       使用rst_n连接到按键,表示复位信号。3.       使用dac_mode信号连接到AD9709的MODE管脚,用来控制其工作模式。4.       使用dac_sleep信号连接到AD9709的SLEEP管脚,用来控制其睡眠模式。5.       使用dac_clka信号连接到AD9709的CLK1管脚,用来控制通道A的时钟。6.       使用dac_wra信号连接到AD9709的WRT1管脚,用来控制通道A的写使能。7.       使用8位信号dac_da连接到AD9709的DB7~0P1管脚,用来控制通道A的写数据。     综上所述,我们这个工程需要7个信号,时钟clk,复位rst_n,dac_mode、dac_sleep、dac_clka、dac_wra和dac_da,其中dac_da是8位信号,其他都是1位信号。     下面表格表示了硬件电路图的连接关系。http://s6.sinaimg.cn/mw690/007n4XDCzy7p2v9Gm2N65 将module的名称定义为dds_da,代码如下: http://s10.sinaimg.cn/mw690/007n4XDCzy7p2vd1UV399      其中clk、rst_n是1位的输入信号,dac_da是8位的输出信号,dac_mode,dac_clka,dac_wra,dac_sleep是一位输出信号。 http://s14.sinaimg.cn/mw690/007n4XDCzy7p2ver0ct2d 3.2   信号设计      我们先分析下DAC的输出。以频率为195312.5Hz的正弦波为例,如下图。频率为195312.5Hz,也就是一个正弦波的周期是5120ns。案例要求一个周期要输出128个点,那就是每隔5120/128=40ns要输出一个点。考虑到工程输入的时钟是50MHz,周期是20ns,那就意味着每隔2个时钟就要输出一个点。     综上所述,产生频率频率为195312.5Hz的正弦波,就是每隔2个时钟输出一个电压值,一共输出128个点,组成一个正弦波。我们要连续产生2个正弦波。     现在进一步分析下,这128个点所对应电压值是多少?由于教学板的输出电压在0.48~2.2V之间,最低值是0.48V,最高值是2.2V。      先将一个标准的正弦波向上平稳1个单位,使得范围变成0~2。然后等间隔取128个点(间隔为2*pi/128),获取其幅度值。我们再用8位信号sin_data表示这些幅度值,其表示方式为:     sin_data = (sin(2*pi/128) + 1) * (255/2),i为0~127     (公式1)http://s11.sinaimg.cn/mw690/007n4XDCzy7p2vji4qu3a       通道A的输出电压 = -1.72 * (DAC /255) + 2.2 V      公式中可以看到,通道A的输出电压是与DAC_DA成线性反比例关系。为了让通道A的电压正确地展现出正弦波,我们还需要做如下调整。      DAC_DA = 255 - sin_data      上面的DAC_DA就是最终输出给DA芯片的数据值,即dac_da信号。     综上所述,产生频率为195312.5Hz的正弦波,就是每隔2个时钟输出一个电压值dac_da。先按表XX每隔1个选出sin_data,再用(255-sin_data)得到dac_da。一共输出128个点,组成一个正弦波,并且连续输出2个正弦波。      以相同的分析方法,分析频率为6.25MHz的正弦波。     频率为6.25MHz,也就是一个正弦波的周期是160ns。案例要求一个周期要输出8个点,那就是每隔160/8=20ns要输出一个点。考虑到工程输入的时钟是50MHz,周期是20ns,那就意味着每隔1个时钟就要输出一个点。     先将一个标准的正弦波向上平稳1个单位,使得范围变成0~2。然后等间隔取8个点(间隔为2*pi/8),获取其幅度值。我们再用8位信号sin_data表示这些幅度值,其表示方式为:sin_data = (sin(2*pi/8) + 1) * (255/2),i为0~7            = (sin(2*pi*16/128) + 1) * (255/2),i为0~7   (公式2)     对比公式1和公式2,发现同样可以由表XX得到相应的sin_data,只是此时间隔16个点取一个值,一共取8个。      综上所述,产生频率为6.25MHz的正弦波,就是每隔1个时钟输出一个电压值dac_da,按表XX中每隔16个点输出一个值,再用(255-sin_data)得到dac_da。一共输出8个点,组成一个正弦波,并且连续产生2个正弦波 .     按同样的分析方法,分析其他频率。最终总结如下:   1.       连续输出2个周期为6.25MHz的正弦波,其中每个正弦波输出8个采样点。      等价于:每隔1个时钟输出一个电压值dac_da,一共输出8个点,组成一个正弦波,连续产生2个正弦波。dac_da的产生方式:表XXX每隔16个选出得到sin_data,通过(255-sin_data)得到dac_da。      2.       连续输出2个周期为3.125MHz的正弦波,其中每个正弦波输出16个采样点。等价于:每隔1个时钟输出一个电压值dac_da,一共输出16个,组成一个正弦波,连续产生2个正弦波。dac_da的产生方式:表XXX每隔8个选出得到sin_data,通过(255-sin_data)得到dac_da。      3.       连续输出2个周期为1.5625MHz的正弦波,其中每个正弦波输出32个采样点。等价于:每隔1个时钟输出一个电压值dac_da,一共输出32个,组成一个正弦波,连续产生2个正弦波。     dac_da的产生方式:表XXX每隔4个选出得到sin_data,通过(255-sin_data)得到dac_da。     4.      连续输出2个周期为781250Hz的正弦波,其中每个正弦波输出64个采样点。等价于:每隔1个时钟输出一个电压值dac_da,一共输出64个点,组成一个正弦波,连续产生2个正弦波。dac_da的产生方式:表XXX每隔2个选出得到sin_data,通过(255-sin_data)得到dac_da。5.       连续输出2个周期为390625Hz的正弦波,其中每个正弦波输出128个采样点。等价于:每隔1个时钟输出一个电压值dac_da,一共输出128个点,组成一个正弦波,连续产生2个正弦波。dac_da的产生方式:表XXX每隔1个选出得到sin_data,通过(255-sin_data)得到dac_da。      6.       连续输出2个周期为195312.5Hz的正弦波,其中每个正弦波输出128个采样点。等价于:每隔2个时钟输出一个电压值dac_da,一共输出128个点,组成一个正弦波,连续产生2个正弦波。dac_da的产生方式:表XXX每隔1个选出得到sin_data,通过(255-sin_data)得到dac_da。       按照至简设计法中的变量法思想,那么可以概括上面的功能:每隔x个时钟输出一个电压值,一共输出y个点,组成一个正弦波,每个要产生要连续产生2个正弦波。由于一共要产生6种不同频率的正弦波,所以还需要一个计数器来数6个。      总结出上面的内容后,我们开始设计代码。“每隔x个时钟输出一个电压值”,所以这需要一个计数器cnt0,加1条件是“1”,结束条件是数到x个,可以得到cnt0的代码。http://s10.sinaimg.cn/mw690/007n4XDCzy7p2vtBEcp69      “一共输出y个点”,这同样需要一个计数器cnt1。注意的是,由于每个点维持x个时钟,也就是cnt1的加1条件是“数到x个时钟”,即end_cnt0。结束条件是:数到y下。可以得到cnt1的代码。http://s12.sinaimg.cn/mw690/007n4XDCzy7p2vvV5sn3b       “每个要产生要连续产生2个正弦波”,这也需要一个计数器cnt2。一个正弦波由y个点组成,所以cnt2的加1条件是“数到y个”,即end_cnt1,结束条件是“数到2个”。可以得到cnt2的代码: http://s12.sinaimg.cn/mw690/007n4XDCzy7p2vwJK8r7b        由于一共要产生6种不同频率的正弦波,所以还需要一个计数器cnt3来数6个。这个cnt3的加1条件是“产生完2个正弦波”,即end_cnt2,结束条件是“数到6个”。可以得到cnt3的代码。 http://s1.sinaimg.cn/mw690/007n4XDCzy7p2vz2rW8c0       我们定义了变量x和y,其中x表示相隔的时钟数,y表示一个正弦波的采样点数。具体的x和y是与正弦波的不同频率相关的,也就是与cnt3相关。根据题意和至简设计法中的变量设计方法,可以得到x和y的代码。 http://s8.sinaimg.cn/mw690/007n4XDCzy7p2vC6sMn77       有了计数器之后,其他信号就可以根据计数器设计出来了。      首先看信号dac_da。dac_da都是按(255-sin_data)得到。那么可以写出dac_da的代码http://s7.sinaimg.cn/mw690/007n4XDCzy7p2vErROud6       接下来看sin_data信号。sin_data是从表XX中选择出来的值,不同的频率,选择的方式不同。那么很自然是定义一个选择信号addr。我们只要控制好addr,就能方便得到sin_data。http://s6.sinaimg.cn/mw690/007n4XDCzy7p2vMWGfba5 http://s4.sinaimg.cn/mw690/007n4XDCzy7p2vNlQyfa3 http://s1.sinaimg.cn/mw690/007n4XDCzy7p2vOc2Zyd0 http://s10.sinaimg.cn/mw690/007n4XDCzy7p2vP6p1Tf9 http://s6.sinaimg.cn/mw690/007n4XDCzy7p2vPv8O195 http://s10.sinaimg.cn/mw690/007n4XDCzy7p2vQhkkV39       接下来设计信号addr。addr是用来控制选择数据的地址,不同频率的正弦波要求地址控制方式不同。频率为6.25MHz(cnt3=0)是每隔16个选择一个;频率为3.125MHz(cnt3=1)是每隔8个点选择一个;频率为1.5625MHz(cnt3=2)是每隔4个点选择一个;频率为781250Hz(cnt3=3)是每隔2个选择一个;频率为390625Hz(cnt3=4)是每隔1个点选择一个;频率为195312.5Hz(cnt3=5)是每隔1个选择一个,一共发送128个。     我们用cnt1表示发送的第几个数。     cnt3==0 时,addr = cnt1*16;     cnt3==1时,addr = cnt1*8;     cnt3==2时,addr = cnt1*4;     cnt3==3时,addr = cnt1*2;     cnt3==4时,addr = cnt1*1;     cnt3==5时,addr = cnt1*1。     因此,可以写得addr的代码http://s5.sinaimg.cn/mw690/007n4XDCzy7p2vUxDLea4        接下来是信号dac_sleep,AD是一直工作的,所以要让dac_sleep一直为0。     dac_clka为了满足tS的时间要求,可以让dac_clka = ~clk。      dac_wra可以与dac_clka相同。      dac_mode是控制AD9709的模式,当为高电平时,表示双通道模式,此时通过DA、DB两组信号分别独立控制两个通道。在能实现功能的前提下,越简单越好,就使用双通道模式,因此令dac_mode一直为1。http://s1.sinaimg.cn/mw690/007n4XDCzy7p2vWs5K820 至此,主体程序已经完成。接下来是将module补充完整。 3.3  信号定义     接下来定义信号类型。     cnt0是用always产生的信号,因此类型为reg。cnt0计数的最大值为15,需要用5根线表示,即位宽是5位。add_cnt0和end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:http://s16.sinaimg.cn/mw690/007n4XDCzy7p2w0KVenef      cnt1是用always产生的信号,因此类型为reg。cnt1计数的最大值为127,需要用8根线表示,即位宽是8位。add_cnt1和end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下: http://s12.sinaimg.cn/mw690/007n4XDCzy7p2w1KqP96b      cnt2是用always产生的信号,因此类型为reg。cnt2计数的最大值为7,需要用3根线表示,即位宽是8位。add_cnt2和end_cnt2都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下:http://s3.sinaimg.cn/mw690/007n4XDCzy7p2w3rqCu12       cnt3是用always产生的信号,因此类型为reg。cnt3计数的最大值为5,需要用3根线表示,即位宽是3位。add_cnt3和end_cnt3都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下:http://s2.sinaimg.cn/mw690/007n4XDCzy7p2w4Qqrvb1      变量x,y是用always方式设计的,因此类型为reg,x最大值为2,要有2位来表示,y最大值为128,要有8根线表示,即位宽为8,因此代码如下, http://s12.sinaimg.cn/mw690/007n4XDCzy7p2w5Id2bab       addr是用always设计的,因此类型为reg。其值最大为127,一共有7根线,位宽为7,故而代码如下 http://s7.sinaimg.cn/mw690/007n4XDCzy7p2w7XWuid6      sin_data是用always设计的,因此类型为reg。其最大值为255,要有8根线表示,位宽为8,故而代码如下:http://s1.sinaimg.cn/mw690/007n4XDCzy7p2wcfM0o20       dac_da是用always设计的,因此类型为reg。其位宽为8;dac_sleep是用assign设计的,因此类型为wire,位宽为1;dac_wra是用assign设计的,因此类型为wire,位宽为1;dac_clka是用assign设计的,因此类型为wire,位宽为1;dac_mode是用assign设计的,因此类型为wire,位宽为1。故而代码如下:http://s7.sinaimg.cn/mw690/007n4XDCzy7p2wcZHbU66        在代码的最后一行写下endmodule.http://s16.sinaimg.cn/mw690/007n4XDCzy7p2wdMx0bdf 4    综合工程和上板4.1 新建工程http://s5.sinaimg.cn/mw690/007n4XDCzy7p2wiR1Yw24    1.)打开quartus,点击File 在File菜单中选择New ProjectWizard.... 。 http://s11.sinaimg.cn/mw690/007n4XDCzy7p2wMB3jIaa        2.弹出Introduction界面选择Next。http://s2.sinaimg.cn/mw690/007n4XDCzy7p2wNOu3Le1       (3)设置工程目录,工程名,顶层模块名      工程目录设置为:D:\mdy_book\dds_da      工程名:dds_da      顶层模块名:dds_da     填写完毕后,点击next之后进入下一界面。http://s13.sinaimg.cn/mw690/007n4XDCzy7p2wQzEdefc      工程类型界面,Project Type选择Empty project,选择空白工程。点Next进入下一个界面。http://s8.sinaimg.cn/mw690/007n4XDCzy7p2wSx7Ib27        (3.)在文件添加界面,点击右上角的,在弹出来的窗口中,双击选择D:\mdy_book\dds_da目录下的dds_da.v文件。 http://s12.sinaimg.cn/mw690/007n4XDCzy7p2wUeDs77b       点击右上角的add按键,将文件添加进工程。http://s14.sinaimg.cn/mw690/007n4XDCzy7p2wVguZv6d       在主窗口中会显示将dds_da.v加入了工程。点击Next,进入下一个界面。 http://s8.sinaimg.cn/mw690/007n4XDCzy7p2wWFtSD07 4.2   综合 http://s9.sinaimg.cn/mw690/007n4XDCzy7p2x0slAc18       在菜单栏中,选中Processing,然后选择Start Compilation,开始对整个工程进行编译和综合。http://s11.sinaimg.cn/mw690/007n4XDCzy7p2x0M3zA0a       出现上面的界面,就说明编译综合成功。 http://s8.sinaimg.cn/mw690/007n4XDCzy7p2x3pANNe7 4.3 配置管脚 http://s3.sinaimg.cn/mw690/007n4XDCzy7p2x6MoH892       在菜单栏中,选中Assignments,然后选择Pin Planner,就会弹出配置管脚的窗口。 http://s16.sinaimg.cn/mw690/007n4XDCzy7p2x7JUhF0f       在配置窗口中的location一列,可以填写每个管脚所对应的FPGA管脚号。      按上面配置好每个信号的管脚,其最终效果如下图。4.4 再次综合http://s6.sinaimg.cn/mw690/007n4XDCzy7p2xsQWd715     在菜单栏中,选中Processing,然后选择Start Compilation,开始对整个工程进行编译和综合。http://s13.sinaimg.cn/mw690/007n4XDCzy7p2xwUpMo3c     出现上面的界面,就说明编译综合成功. 4.5   连接开发板 http://s14.sinaimg.cn/mw690/007n4XDCzy7p2xGmU0tcd       连接示意如上图所示。将电源接上开发板;USBBLASTER一端连接到JTAG插口,另一端连到PC的USB接口;将开发板上的P7接口与示波器相连。最后再将电源打开。 4.6  上板 http://s10.sinaimg.cn/mw690/007n4XDCzy7p2xKuGWl99       在quartus的Task窗口中,右键Program Device 选择Open 进入烧录界面。http://s1.sinaimg.cn/mw690/007n4XDCzy7p2xLa4lqc0 在上面的界面中,默认会选中文件output/dds_da.sof,如果没有生成请看XXXX。http://s15.sinaimg.cn/mw690/007n4XDCzy7p2xNFodw4e 点击statr,在progress这一条显示100%即表示成功,此时可以看FPGA输出效果了。<p> 此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2018-11-15
  • 发表了主题帖: 分享贴:刚完成的FPGA插值滤波器设计

    1    项目背景 (源码下载 交流辅导群:544453837)1.1 多采样率数字滤波器      多采样率就是有多个采样率的意思。前面所说的FIR,IIR滤波器都是只有一个采样频率,是固定不变的采样率,然而有些情况下需要不同采样频率下的信号。      按照传统的速率转换理论,我们要实现采样速率的转换,可以这样做,假如有一个有用的正弦波模拟信号,AD采样速率是f1,现在我需要用到的是采样频率是f2的信号,传统做法是将这个经过f1采样后的信号进行DA转换,再将转换后的模拟信号进行以f2采样频率的抽样,得到采样率为f2的数字信号,至此完成采样频率的转换      但是这样的做法不仅麻烦,而且处理不好的话会使信号受到损伤,所以这种思想就被淘汰了,现在我们用到的采样率转换的方法就是抽取与内插的思想。1.2 抽取     先来总体来解释一下抽取的含义:前面不是说,一个有用的正弦波模拟信号经采样频率为f1的抽样信号抽样后得到了数字信号,很明显这个数字信号序列是在f1频率下得到的,现在,假如我隔几个点抽取一个信号,比如就是5吧,我隔5个点抽取一个信号,是不是就是相当于我采用了1/5倍f1的采样频率对模拟信号进行采样了?所以,抽取的过程就是降低抽样率的过程,但是我们知道,这是在时域的抽样,时域的抽样等于信号在频域波形的周期延拓,周期就是采样频率,所以,为了避免在频域发生频谱混叠,抽样定理也是我们要考虑的因素    下面来具体来介绍http://s15.sinaimg.cn/mw690/007n4XDCzy7p2C2a0Ds9e        如上图所示,假如上面就是某一有用信号经采样频率f1抽样得到的频谱,假设这时候的采样频率为8Khz,可以通过数格子得到,从0到F1处有8个空格,每个空格代表1Khz,有些朋友可能会问,这不是在数字频域吗,单位不是π吗,哪来的hz?是的,这里是数字频域,采样频率F1处对应的是2π,这里只是为了好解释,我们用模拟频率来对应数字频率。http://s16.sinaimg.cn/mw690/007n4XDCzy7p2C4vTYz0f       上面是采样频率为8K的数字信号频域图,现在我要对这个数字信号进行时域抽取,从而来降低信号的采样率,我们知道,一旦我们对数字信号进行时域抽取,那么采样率下降,而采样率就是数字信号频域的波形周期,那么也就是周期下降,所以,我们对信号进行抽取要有个度,要在满足抽样定理的条件下对信号进行抽取,否则就会发生频谱混叠。      上图就是对信号进行了1/5倍的F1采样频率抽取,可见,由于发生了频谱混叠现象,因为1/5倍的F1是1600hz,而信号的频带是1000hz,不满足抽样定理,导致发生了频谱混叠,所以,为了避免发生这种情况,除了要满足抽样定理之外,即抽样倍数不能太高,我们还需要把信号的频带设置在F1/2以下,才能确保信号不发生频谱混叠,因此,我们需要在抽取之前加一个低通滤波器,书上叫做抗混叠低通滤波器,用来限制信号的频带,然后再进行抽取,这样的话我们来算一下     低通滤波器的截止频率就是1/2倍的经抽取后的采样速率,即fc = 1/2 * (F1/M) ,M是抽取倍数。而1/2*F1对应的数域频率是π,因此我们得出,     抗混叠低通滤波器的截止频率是π/M1.3  内插     抽取的过程是降低采样率的过程,那么插值的过程当然就是提高采样率的过程。大体的思路可以这么理解,我们将经f1抽样下得到的数字信号的每两个点之间进行插值,插入的值是0,插值之后,信号在单位时间内的采样点数增多,当然也就是采样速率的提升,采样速率提升后我们知道,那么信号的频谱的周期就会增加http://s12.sinaimg.cn/mw690/007n4XDCzy7p2C9GKgj2b       需要注意的一点就是,插值前后,我们只是在时域信号中间插入了D-1个零值,仅仅是改变了采样率,并没有改变信号的信息,因此,在频域,信号频谱的形状是不会改变的,改变的仅仅是周期,如上图,F1是插值之前信号的周期,插值之后,信号频谱的形状不变,周期成了F1*D,D是插值倍数。如果我们直接用F1*D倍的采样率采信号,得到的频谱会发现,就不会有中间两个波形,因此,这两个波形是多余的,书上叫做是镜像频谱。既然是多余的,我们就可以将它用一个低通滤波器滤掉,这样的低通滤波器,就叫做镜像低通滤波器。这样我们来计算一下镜像低通滤波器的截止频率http://s4.sinaimg.cn/mw690/007n4XDCzy7p2Cav8Zle3       根据上面这张图我们可以求出镜像低通滤波器的截止频率,可以看到,fc = 1/2 *F1,这里我们假设,内插之后的采样频率为F2=F1*D,那么,fc =1/2*(F2/D),而1/2*F2对应的是π,注意,这里是1/2*F2对应π,不是1/2*F1了,因为这已经是插值之后采样率增加之后的频谱了,所以我们得出:       镜像低通滤波器的截止频率为:π/D 1设计目标本次案例将使用到采样率大于100M的双通道的示波器。将示波器的两个通道,分别与FPGA的DA通道1和DA通道2相连,观察两路DA的输出。其连接示意如下图所示:http://s6.sinaimg.cn/mw690/007n4XDCzy7p2Ce6qTbb5      本案例是FPGA内部产生正弦信号,这个正弦信号一路输出给DA通道A,另一路经过插值滤波器后,输出给DA通道B。http://s9.sinaimg.cn/mw690/007n4XDCzy7p2CeSzgsf8      正弦信号产生电路产生频率为62.5KHz的正弦信号,该正弦信号由8个点组成。     插值滤波器是4倍的插值,也就是说进来是8个点的正弦波,输出将是32个的正弦波。    仿真效果,上面的波形为插值前,下面的为插值后可以明显看出下面的波形更为圆滑。http://s8.sinaimg.cn/mw690/007n4XDCzy7p2ChUreT97     下面是示波器的显示效果 http://s16.sinaimg.cn/mw690/007n4XDCzy7p2CipSuHdf 3   设计实现3.1 顶层信号     新建目录:D:\mdy_book\cic_prj。在该目录中,新建一个名为cic_prj.v的文件,并用GVIM打开,开始编写代码。     我们要实现的功能,概括起来就是FPGA产生控制AD9709,让其中的通道A未滤波的正弦信号,让通道B输出滤波后的正弦信号。为了控制AD9709的工作模式,就要控制AD9709的MODE、SLEEP管脚;为了控制通道A,就需要控制AD9729的CLK1、WRT1、DB7~0P1管脚;为了控制通道B,就需要控制AD9729的CLK2、WRT2、DB7~0P2管脚。根据设计目标的要求,整个工程需要以下信号:1.       使用clk连接到晶振,表示50M时钟的输入。2.       使用rst_n连接到按键,表示复位信号。3.       使用dac_mode信号连接到AD9709的MODE管脚,用来控制其工作模式。4.       使用dac_sleep信号连接到AD9709的SLEEP管脚,用来控制其睡眠模式。5.       使用dac_clka信号连接到AD9709的CLK1管脚,用来控制通道A的时钟。6.       使用dac_wra信号连接到AD9709的WRT1管脚,用来控制通道A的写使能。7.       使用8位信号dac_da连接到AD9709的DB7~0P1管脚,用来控制通道A的写数据。8.       使用dac_clkb号连接到AD9709的CLK2脚,用来控制通道B时钟。9.       使用dac_wrb号连接到AD9709的WRT2脚,用来控制通道B使能。10.    使用8位信号dac_db接到AD9709的DB7~0P2脚,用来控制通道B写数据。      综上所述,我们这个工程需要10个信号,时钟clk,复位rst_n,dac_mode、dac_sleep、dac_clka、dac_wra、dac_da、dac_clkb、dac_wrb和dac_db信号,其中dac_da和dac_db是8位信号,其他都是1位信号。下面表格表示了硬件电路图的连接关系。http://s14.sinaimg.cn/mw690/007n4XDCzy7p2CnAmq93d     将module的名称定义为cic_prj,代码如下: http://s2.sinaimg.cn/mw690/007n4XDCzy7p2ColMsNd1       其中clk、rst_n是1位的输入信号,dac_da和dac_db是8位的输出信号,dac_mode,dac_clka,dac_wra,dac_sleep,dac_clkb,dac_wrb是一位输出信号。http://s5.sinaimg.cn/mw690/007n4XDCzy7p2Cr8eIAb4 3.2  正弦信号设计     假设产生的正弦信号命名为sin_data信号。sin_data一共有8个值,是从一个正弦信号中,按(2*pi/8)的间隔采样到的,可列出下表。http://s12.sinaimg.cn/mw690/007n4XDCzy7p2CsgPZV4b        很自然地定义一个7位的选择信号addr。我们只要控制好addr,就能方便得到sin_data。因此可以写出下面代码。 http://s14.sinaimg.cn/mw690/007n4XDCzy7p2CuRkDP6d       接下来是设计信号addr。     addr是用来控制选择数据的地址,通过控制addr的增加值,就能产生所需要的正弦波。     本案例要求产生62.5KHz的正弦信号。该正弦信号的周期是16000ns。本工程的工作时钟是20ns,也就是16000/20 = 800个时钟输出一个正弦信号,也就是800个时钟将上表的8个值输出一遍,即每100个时钟输出addr加1。    每100个时钟输出一个值,那意味着我们需要一个计数器cnt0,该计数器用来对这100进行计数。计数器的加1条件是“1”,结束条件是“数到100个”。因此可写出cnt0的代码。http://s1.sinaimg.cn/mw690/007n4XDCzy7p2CvFMwo80      每100个时钟后,addr就加1。说明这个addr也是一个计数器,该计数器的加1条件是“数到100个时钟”,即end_cnt0,结束条件是“数到8个”。http://s4.sinaimg.cn/mw690/007n4XDCzy7p2CAIOT943 3.3    CIC  滤波器设计3.3.1 新建FPGA工程http://s15.sinaimg.cn/mw690/007n4XDCzy7p2CEdY507e 1.)打开quartus,点击File 在File菜单中选择New ProjectWizard.... 。 http://s13.sinaimg.cn/mw690/007n4XDCzy7p2CFhTdy6c      2.弹出Introduction界面选择Next。http://s13.sinaimg.cn/mw690/007n4XDCzy7p2CHlrk08c     (3)设置工程目录,工程名,顶层模块名     工程目录设置为:D:\mdy_book\cic_prj     工程名:cic_prj     顶层模块名:cic_prj      填写完毕后,点击next之后进入下一界面。http://s7.sinaimg.cn/mw690/007n4XDCzy7p2CIjGbIe6      (3.)在文件添加界面,不选择任何文件。点击Next,进入下一个界面。工程类型界面,Project Type选择Empty project,选择空白工程。点Next进入下一个界面。 http://s12.sinaimg.cn/mw690/007n4XDCzy7p2CKATWz0b (3.)在文件添加界面,不选择任何文件。点击Next,进入下一个界面。 http://s7.sinaimg.cn/mw690/007n4XDCzy7p2CLuTZkf6 (4.)器件选择界面。在Device family这一项之中选择 Cyclone IV E;在下部的Available device 选择EP4CE6F23C8。完成后直接点击Finish。 3.3.2FPGA生成CIC IP核http://s10.sinaimg.cn/mw690/007n4XDCzy7p2CRhO5j19        建立工程后,在quartus中IP catalog这一界面中选择DSP下一目录中选择Filter 再选择 CIC。http://s1.sinaimg.cn/mw690/007n4XDCzy7p2CTfmQE50       点击后进入此界面给新生成的fir滤波器ip核选择如下路径:D:\mdy_book\cic_prj,entityname填写:my_cic。点击OK后,进入FIR滤波器设置界面。http://s8.sinaimg.cn/mw690/007n4XDCzy7p2CVxVj147      按如下设置:     Filter Type:要选择Interpolator,表示是插值滤波器。    Rate change factor:填上4,表示是4倍插值。     output Rounding Method:选择Truncation,表示输出的结果要截断。    Output data width:选择8。表示输出结果要截断为8位。    其他选项默认,点击窗口右下角的Generate Hdl,会弹出下面的窗口。 http://s9.sinaimg.cn/mw690/007n4XDCzy7p2CWTsq4e8      注意选择文件是Verilog文件,其他都不用勾选。点击Generate,就会生成y_cic的verilog文件。http://s4.sinaimg.cn/mw690/007n4XDCzy7p2CZah3Be3       出现上面的提示,就是生成成功了。     点Finish关闭CIC滤波器生成窗口。http://s2.sinaimg.cn/mw690/007n4XDCzy7p2CZUeOdb1       如果出现上面的提示,就是表示要手动将刚才生成的IP核加到本工程。http://s3.sinaimg.cn/mw690/007n4XDCzy7p2D21TMec2        在Project菜单中选择Add/Remove File to Project,弹出文件窗口。http://s16.sinaimg.cn/mw690/007n4XDCzy7p2D2UjHF5f       点击右上角的,在弹出来的窗口中,双击选择D:\mdy_book\cic_prj\my_cic\synthesis目录下的my_cic.qip文件(注意不要搞错文件类型)。然后记得要点Add,才算正式加到工程。http://s2.sinaimg.cn/mw690/007n4XDCzy7p2D6bqNPa1 点OK关闭本窗口。IP核生成后弹出此对话框点击yes 将此IP核添加进工程。3.3.3   例化CIC IP核      用GVIM打开D:\mdy_book\cic_prj\my_cic\synthesis\my_cic.v文件,该文件就是生成的CIC IP核文件http://s16.sinaimg.cn/mw690/007n4XDCzy7p2D9lXDV9f http://s8.sinaimg.cn/mw690/007n4XDCzy7p2DapzOTd7         特别注意的是,滤波器的输入数据和输出数据都是有符号数(补码的形式,-128~127)。而我们知道,正弦信sin_data是无符号数(0~255)。所以要将sin_data变成有符号数,再送给FIR进行滤波。假设转换后的信号为cic_din,该信号位宽为8位。      无符号数转成有符号数的方法很简单:cic_din = sin_data - 128。读者有兴趣可以验证一下。      生成CIC IP核后,我们要对其进行例化,才行使用上这个IP核,例化名起名u_my_cic,cic的输出数据信号命名为cic_dout。       我们要控制CIC IP核的输出,使每个数据都能等间隔输出数据。由于CIC滤波器的输入是100个时钟一个数据,CIC是4倍速率,因此输出是25个时钟一个数据。所以我们每25个时钟给一个有效信号连到out_ready接口上。这时需要一个计数器cnt1来计时25个时钟,该计数器加1条件是“1”,结束条件是“数到25个”。http://s12.sinaimg.cn/mw690/007n4XDCzy7p2Dg9gaTbb     有了这些信号后,就可以例化CIC IP核了。http://s3.sinaimg.cn/mw690/007n4XDCzy7p2DhAxy2d2 3.4 DA接口信号设计       接下来是设计信号dac_da。dac_da是直接输出正弦信号,但由于DA的输出电压与dac_da是成反比例线性关系,所以dac_da都是按(255-sin_data)得到。那么可以写出dac_da的代码。http://s1.sinaimg.cn/mw690/007n4XDCzy7p2Dm4u40d0       接下来是设计信号dac_sleep,AD是一直工作的,所以要让dac_sleep一直为0。      dac_clka为了满足tS的时间要求,可以让dac_clka = ~clk。     dac_wra可以与dac_clka相同。http://s14.sinaimg.cn/mw690/007n4XDCzy7p2Dntahv0d        接下来是设计信号dac_db。dac_db是直接输出滤波后的信号cic_dout。但要注意的是cic_dout是有符号数(范围是-128~127),所以要转有无符号数(0~255)。假设转换后的信号为cic_dout2,则cic_dout2 = cic_dout + 128。另外,由于DA的通道2的输出电压与dac_db是成反比例线性关系,所以dac_db都是按(255-cic_dout2)得到。那么可以写出dac_db的代码。http://s8.sinaimg.cn/mw690/007n4XDCzy7p2DoAPNt47       dac_clkb为了满足tS的时间要求,可以让dac_clkb = ~clk。     dac_wrb可以与dac_clkb相同。http://s4.sinaimg.cn/mw690/007n4XDCzy7p2Dr7Y9dc3 3.5   信号定义      至此,模块主体已经完成。接下来是将module补充完整。    cnt0是用always产生的信号,因此类型为reg。cnt0计数的最大值为99,需要用7根线表示,即位宽是7位。add_cnt0和end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下: http://s9.sinaimg.cn/mw690/007n4XDCzy7p2Dy1iukd8      cnt1是用always产生的信号,因此类型为reg。cnt1计数的最大值为24,需要用5根线表示,即位宽是5位。add_cnt1和end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下:http://s12.sinaimg.cn/mw690/007n4XDCzy7p2DzcHkD9b       addr是用assign设计的,因此类型为wire。其值最大为7,一共有3根线,位宽为3;add_addr和end_addr都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。故而代码如下http://s16.sinaimg.cn/mw690/007n4XDCzy7p2DBhp5t9f       sin_data是用always设计的,因此类型为reg。其最大值为255,要有8根线表示,位宽为8,故而代码如下http://s9.sinaimg.cn/mw690/007n4XDCzy7p2DCSlOo58      cic_din是用assign设计的,因此类型为wire。其位宽为8,故而代码如下http://s8.sinaimg.cn/mw690/007n4XDCzy7p2DE2YSja7      cic_dout是例化模块的输出,非always设计的,因此类型为wire。其位宽为8,故而代码如下 http://s8.sinaimg.cn/mw690/007n4XDCzy7p2DGDYkD77     cic_dout2是用assign设计的,非always设计的,因此类型为wire。其位宽为8,故而代码如下http://s8.sinaimg.cn/mw690/007n4XDCzy7p2DHMjIP77        dac_da是用always设计的,因此类型为reg。其位宽为8;dac_sleep是用assign设计的,因此类型为wire,位宽为1;dac_wra是用assign设计的,因此类型为wire,位宽为1;dac_clka是用assign设计的,因此类型为wire,位宽为1;dac_mode是用assign设计的,因此类型为wire,位宽为1。故而代码如下http://s12.sinaimg.cn/mw690/007n4XDCzy7p2DKzxt97b      dac_db是用always设计的,因此类型为reg。其位宽为8;dac_wrb是用assign设计的,因此类型为wire,位宽为1;dac_clkb是用assign设计的,因此类型为wire,位宽为1。故而代码如下。 http://s15.sinaimg.cn/mw690/007n4XDCzy7p2DLjhlcde        在代码的最后一行写下endmodulehttp://s12.sinaimg.cn/mw690/007n4XDCzy7p2DMiYOfab     至此,整个代码的设计工作已经完成。下一步是新建工程和上板查看现象 4    综合与上板4.1 添加文件到工程http://s1.sinaimg.cn/mw690/007n4XDCzy7p2E3BZF6f0 ​      1.)前面已经介绍了新建工程。现在打开quartus,在Project菜单中选择Add/Remove File to Project,弹出文件窗口。http://s3.sinaimg.cn/mw690/007n4XDCzy7p2E4YrxU02 ​     点击右上角的,在弹出来的窗口中,双击选择D:\mdy_book\fir_prj目录下的fir_prj.v文件。然后记得要点Add,才算正式加到工程。     点OK关闭本窗口。4.2  综合http://s2.sinaimg.cn/mw690/007n4XDCzy7p2E8DGlH31 ​     在菜单栏中,选中Processing,然后选择Start Compilation,开始对整个工程进行编译和综合。http://s15.sinaimg.cn/mw690/007n4XDCzy7p2E9szBc3e ​     出现上面的界面,就说明编译综合成功。4.3  配置管脚http://s5.sinaimg.cn/mw690/007n4XDCzy7p2EcXPOAf4 ​在菜单栏中,选中Assignments,然后选择Pin Planner,就会弹出配置管脚的窗口。http://s10.sinaimg.cn/mw690/007n4XDCzy7p2EdQxU549      在配置窗口中的location一列,可以填写每个管脚所对应的FPGA管脚号。     按上面配置好每个信号的管脚,其最终效果如下图。http://s8.sinaimg.cn/mw690/007n4XDCzy7p2Eg8IE727      按上面配置好每个信号的管脚,其最终效果如下图。 http://s8.sinaimg.cn/mw690/007n4XDCzy7p2Ei6xoz67 关闭Pin Planner,软件自动会保存管脚配置信息。4.4  再次综合http://s6.sinaimg.cn/mw690/007n4XDCzy7p2EkTylL55 在菜单栏中,选中Processing,然后选择Start Compilation,开始对整个工程进行编译和综合。http://s14.sinaimg.cn/mw690/007n4XDCzy7p2Eqtc8Bfd ​     出现上面的界面,就说明编译综合成功。4.5   连接开发板http://s9.sinaimg.cn/mw690/007n4XDCzy7p2ErOhYcf8 ​      连接示意如上图所示。将电源接上开发板;USBBLASTER一端连接到JTAG插口,另一端连到PC的USB接口;将开发板上的P7接口和P11与示波器的两个通道相连。最后再将电源打开。4.6   上板http://s10.sinaimg.cn/mw690/007n4XDCzy7p2Ethwtre9 ​     在quartus的Task窗口中,右键Program Device 选择Open 进入烧录界面。http://s14.sinaimg.cn/mw690/007n4XDCzy7p2Eu9F7L1d ​在上面的界面中,默认会选中文件output/fir_prj.sof,如果没有生成请看XXXX。http://s16.sinaimg.cn/mw690/007n4XDCzy7p2EvLFXF8f 在上面的界面中,Hardware Setup的旁边会显示:USB-Blaster。如果不是,请看XXXX。     点击statr,在progress这一条显示100%即表示成功,此时可以看FPGA输出效果了。 此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2018-11-05
  • 发表了主题帖: 明德扬FPGA连载课程第一阶段第三章VERILOG(1)

    1.1 传统的数字系统设计方式—原理图设计       在传统的设计方法中,当设计工程师设计一个新的硬件、一个新的数字电路或一个数字逻辑系统时,他或许在CAE 工作站上做设计,为了能在CAE工作站做设计,设计者必须为设计画一张线路图,通常地,线路图是由表示信号的线和表示基本设计单元的符号连在一起组成线路图,符号取自设计者用于构造线路图的零件库。若设计者是用标准逻辑器件(如74系列等)做板极设计线路图,那么在线路图中,符号取自标准逻辑零件符号库;若设计是进行ASIC设计,则这些符号取自ASIC库的可用的专用宏单元。这就是传统的原理图设计方法。对线路图的逻辑优化,设计者或许利用一些EDA工具或者人工地进行逻辑的布尔函数逻辑优化。为了能够对设计进行验证,设计者必须通过搭个硬件平台(如电路板),对设计进行验证。1.2 硬件语言描述方式—verilog       随着电子设计技术的飞速发展,设计的集成度、复杂度越来越高,传统的设计方法已满足不了设计的要求,因此要求能够借助当今先进的EDA工具,使用一种描述语言,对数字电路和数字逻辑系统能够进行形式化的描述,这就是硬件描述语言。硬件描述语言HDL(Hardware  Description Language )是一种用形式化方法来描述数字电路和数字逻辑系统的语言。数字逻辑电路设计者可利用这种语言来描述自己的设计思想,然后利用EDA工具进行仿真,再自动综合到门级电路,最后用ASIC或FPGA实现其功能。举个例子,在传统的设计方法中,对2输入的与门,我们可能需到标准器件库中调个74系列的器件出来,但在硬件描述语言中,“&”就是一个与门的形式描述,“C = A & B”就是一个2输入与门的描述。而“and ”就是一个与门器件。硬件描述语言发展至今已有二十多年历史,当今业界的标准中(IEEE标准)主要有VHDL和 Verilog HDL 这两种硬件描述语言。       Verilog HDL 语言最初是于1983 年由Gateway DesignAutomation 公司为其模拟器产品开发的硬件建模语言。那时它只是一种专用语言。由于他们的模拟、仿真器产品的广泛使用,Verilog HDL 作为一种便于使用且实用的语言逐渐为众多设计者所接受。在一次努力增加语言普及性的活动中,Verilog HDL 语言于1990 年被推向公众领域。Open Verilog International(O V I )是促进 Verilog 发展的国际性组织。1992 年,OVI 决定致力于推广Verilog OVI 标准成为IEEE 标准。这一努力最后获得成功,Verilog 语言于1995 年成为IEEE 标准,称为IEEE Std1364-1995 。完整的标准在Verilog 硬件描述语言参考手册中有详细描述。       Verilog HDL即verilog硬件硬件描述语言,它主要应用于数字电路和系统设计、数字电路和系统仿真等,即利用计算机和相关软件对VERILOG HDL等硬件语言建模的复杂数字电路设计进行仿真验证,再利用综合软件将设计的数字电路自动综合,以得到符合功能需求并且在相应的硬件结构止可以映射实现的数字逻辑网表,然后布局布线,根据网表和选定的实现器件工艺特性自动生成具体电路,同时软件生成选定器件的延时模型,经过仿真验证确定无误后,用来制造ASIC芯片或者写入FPGA和CPLD器件中,最终实现电路设计。        Verilog HDL 语言具有下述描述能力:设计的行为特性、设计的数据流特性、设计的结构组成以及包含响应监控和设计验证方面的时延和波形产生机制。所有这些都使用同一种建模语言。此外,Verilog HDL 语言提供了编程语言接口,通过该接口可以在模拟、验证期间从设计外部访问设计,包括模拟的具体控制和运行。 Verilog HDL 语言不仅定义了语法,而且对每个语法结构都定义了清晰的模拟、仿真语义。因此,用这种语言编写的模型能够使用Verilog 仿真器进行验证。语言从C 编程语言中继承了多种操作符和结构。Verilog HDL 提供了扩展的建模能力,其中许多扩展最初很难理解。但是,Verilog HDL 语言的核心子集非常易于学习和使用,这对大多数建模应用来说已经足够。当然,完整的硬件描述语言足以对从最复杂的芯片到完整的电子系统进行描述。2 综合器和仿真器http://s16.sinaimg.cn/mw690/007n4XDCzy7oNBw3gHd3f        Verilg是硬件描述语言,顾名思义,就是用代码的形式描述硬件的功能。而我们最终是要在电路上实现该功能的。当Verilog描述出硬件功能后,我们需要综合器对Verilog代码进行解释,将代码转化成实际的电路来表示,最终实际的电路,我们称之为网表。这种将Verilog代码转成网表的工具,就是综合器。上图左上角是一份verilog代码,该代码描述了一个加法器功能。该代码经过综合器解释后,转化成一个加法器电路。QUARTUS、ISE和VIVADO都是综合器,集成电路常用的综合器是DC。       我们在FPGA设计的过程中,不可避免会出现各种BUG。如果我们编写好代码,综合成电路,烧写到FPGA后,才看到问题,此时去定位问题就会非常地困难了。在综合前,我们可以在电脑里对代码进行仿真测试一下,把BUG找出来解决,最后才烧写进FPGA。我们可以认为,没有经过仿真验证的代码,一定是存在BUG的。       为了模拟真实的情况,我们需要编写测试文件。该文件也是用verilog编写的,描述了仿真对象的输入激励情况。该激励力求模仿最真实的情况,产生最将近的激励信号,将该信号的波形输入给仿真对象,查看仿真对象的输出是否与预期一致。       为了做仿真验证,我们编写了测试文件。将测试文件和被测试对象加入到仿真器中。仿真器对测试文件和被测试对象的代码进行解释。根据测试文件,产生测试激励,输入给被测试对象;根据补测试对象的代码,产生被测试对象的输出。需要注意的是,在仿真过程中,没有将代码转成电路,仿真器只是对代码进行仿真验证。至于该电路是否可转成电路,仿真器是不关心的。常用的仿真器是MODELSIM和VCS等。        由此可见,verilog的代码不仅可以描述电路,还可以用于测试。事实上,Verilog定义的语法非常之多,但绝大部分都是为了仿真测试来使用的。只有少部分才是用于电路设计,详细可以参考本书的“可综合逻辑设计”一节。       Verilog中用于设计的语法,才是学习的重点。掌握好设计的语法,熟练应用于各种复杂的项目,这是技能的核心。其他测试用的语法,需要时查找和参考就已经足够了。本书着重点,是用于本科、研究生的教学用途,因此将重点讲解设计用的语法。 3 Verilog HDL 基本语法      本节介绍Verilog HDL 语言的一些基本要素,包括标识符、注释、格式、数字值集合、两种数据类型、运算符和表达式和一些基本的语句如IF语句等。3.1 标识符       verilog HDL的源文本文件是由一串词法标识符构成的,一个词法标识符包含一个或若干个字符。源文件中这些标识符的排放格式很自由,也就是说,在句法上间隔和换行只是将这些标识符分隔开来并不具有重要意义,转意标识符除外。        verilog HDL语言中词法标识符的类型有以下几种:间隔符、注释符、算子、数值、字符串、标识符、关键词。      接下来对这些标识符一一进行说明 3.1.1间隔符      间隔符包括空格字符,制表符,换行以及换页符。这些字符除了起到与其它词法标识符相分隔的作用外可以被忽略。      注意的是,在“字符串”中的空白和制表符会被认为是有意义的字符。 3.1.2注释符       Verilog HDL 中有两种注释的方式:单行注释和段注释(多行)。      单行注释以两个字符“//”起始,以新的一行作为结束。也就是注释一行。      段注释是以“”结束,在两个符号之间的语句都是注释语句,因此可扩展到多行。如:以上n个语句都是注释语句。      段注释不允许嵌套,在段注释中单行注释标识符“//”没有任何特殊意义。 3.1.3算子      算子是由单个、两个或三个字符组成的序列串,分别称之为一元算子、二元算子和条件算子。它用在表达式中。      例如“|a”等都是一元算子。该例子是“a的所有比特相或”。假如a是三位信号并且值为011,则“|a”就是“0|1|1”等于1。      例如“a & b”、“a|b等是二元算子。该两个例子分别是a与b按位相与、a与b按位相或。      例如“a==1?b:c”,则是条件算子。该例子是说“当a等于1时,选择b,否则选择c”。 3.1.4标识符      标识符是用户在描述时给Verilog对象起的名字,用这个标识符来提及相应的对象。      标识符可以是字母、数字、$符和下划线_字符的任意组合序列,但它必须以字母(a-z,A-Z)或下划线(_)开头,不能是数字或$符。后面可以是字母、数字或者_。例如abc_123,_abc等。非法命名如下_abc,24abc。标识符最长可以是1023个字符。      标识符是区分大小写的。例如sel和SEL是不同的标识符。 3.1.5关键字      Verilog HDL 定义了一系列保留字,叫做关键词。这些关键字都有一定的用途。a.   下表列出了语言中的所有保留字。b.   注意只有小写的关键词才是保留字。例如,标识符always (这是个关键词)与标识符ALWAYS(非关键词)是不同的。c.   在给信号命名时,不要用关键字。 http://s10.sinaimg.cn/mw690/007n4XDCzy7oNBJDyxrd9 3.1.6系统任务标识符      系统任务标识符:$,其中$表示引入一个语言结构,其后所跟的标识符是系统任务或系统函数的名称。      系统功能可以执行不同的操作:---实时显示当前仿真时间($time)---显示/监视信号的值($display,$monitor)---暂停仿真($stop)---结束仿真($finish)例: $monitor($time,”a=%b,b=%h”,a,b)      每次a或b信号的值发生变化,这一系统任务的调用负责显示当前仿真时间,二进制格式的a信号和十六进制格式的b信号。       注意,系统任务还有很多,在此不一一列出。但系统任务一般用于仿真测试用途,读者没必要重点学习,在学习工作过程中,如果需要用到时,再查找一下就可以了。      注:(本博客连载的内容将出版成图书,并将录制视频,免费公开学习,欢迎大家留意。本连载前面是基础部分,与一般教材无异,后面是项目实践,是本连载的特色。如果你有一定的基础(能看懂verilog代码即可),那么可跳过前面部分,直接学习后面的项目实践。      项目实践将有16个,从基础的闪烁灯开始,到最后是信号处理的项目,如信号发生器、FIR滤波器、插值滤波器和AD采集等。      本连载学习效果:不难看能懂代码,还能知道每一行代码怎么写,怎么设计)此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2018-11-02
  • 发表了主题帖: 明德扬FPGA连载课程第一阶段第二章FPGA设计流程

    FPGA的设计流程就是利用EDA开发软件和编程工具对FPGA芯片进行开发的过程。典型FPGA的开发流程一般如下图所示,包括功能定义/器件选型、设计输入、功能仿真、综合优化、综合后仿真、实现、布线后仿真、板级仿真以及芯片编程与调试等主要步骤。http://s6.sinaimg.cn/mw690/007n4XDCzy7orE3GAdL85 第一节:功能定义/器件选型    在FPGA设计项目开始之前,必须有系统功能的定义和模块的划分,另外就是要根据任务要求,如系统的功能和复杂度,对工作速度和器件本身的资源、成本、以及连线的可布性等方面进行权衡,选择合适的设计方案和合适的器件类型。一般都采用自顶向下的设计方法,把系统分成若干个基本单元,然后再把每个基本单元划分为下一层次的基本单元,一直这样做下去,直到可以直接使用EDA元件库为止。第二节:设计输入     设计输入是将所设计的系统或电路以开发软件要求的某种形式表示出来,并输入给EDA工具的过程。常用的方法有硬件描述语言(HDL)和原理图输入方法等。原理图输入方式是一种最直接的描述方式,在可编程芯片发展的早期应用比较广泛,它将所需的器件从元件库中调出来,画出原理图。这种方法虽然直观并易于仿真,但效率很低,且不易维护,不利于模块构造和重用。更主要的缺点是可移植性差,当芯片升级后,所有的原理图都需要作一定的改动。目前,在实际开发中应用最广的就是HDL语言输入法,利用文本描述设计,可以分为普通HDL和行为HDL。普通HDL有ABEL、CUR等,支持逻辑方程、真值表和状态机等表达方式,主要用于简单的小型设计。而在中大型工程中,主要使用行为HDL,其主流语言是Verilog HDL和VHDL。这两种语言都是美国电气与电子工程师协会(IEEE)的标准,其共同的突出特点有:语言与芯片工艺无关,利于自顶向下设计,便于模块的划分与移植,可移植性好,具有很强的逻辑描述和仿真功能,而且输入效率很高。除了这IEEE标准语言外,还有厂商自己的语言。也可以用HDL为主,原理图为辅的混合设计方式,以发挥两者的各自特色。第三节:功能仿真     功能仿真也称为前仿真是在编译之前对用户所设计的电路进行逻辑功能验证,此时的仿真没有延迟信息,仅对初步的功能进行检测。仿真前,要先利用波形编辑器和HDL等建立波形文件和测试向量(即将所关心的输入信号组合成序列),仿真结果将会生成报告文件和输出信号波形,从中便可以观察各个节点信号的变化。如果发现错误,则返回设计修改逻辑设计。常用的工具有Model Tech公司的ModelSim、Sysnopsys公司的VCS和Cadence公司的NC-Verilog以及NC-VHDL等软件。第四节:综合优化      所谓综合就是将较高级抽象层次的描述转化成较低层次的描述。综合优化根据目标与要求优化所生成的逻辑连接,使层次设计平面化,供FPGA布局布线软件进行实现。就目前的层次来看,综合优化(Synthesis)是指将设计输入编译成由与门、或门、非门、RAM、触发器等基本逻辑单元组成的逻辑连接网表,而并非真实的门级电路。真实具体的门级电路需要利用FPGA制造商的布局布线功能,根据综合后生成的标准门级结构网表来产生。为了能转换成标准的门级结构网表,HDL程序的编写必须符合特定综合器所要求的风格。由于门级结构、RTL级的HDL程序的综合是很成熟的技术,所有的综合器都可以支持到这一级别的综合。常用的综合工具有Synplicity公司的Synplify/Synplify Pro软件以及各个FPGA厂家自己推出的综合开发工具。5 第五节:综合后仿真      综合后仿真检查综合结果是否和原设计一致。在仿真时,把综合生成的标准延时文件反标注到综合仿真模型中去,可估计门延时带来的影响。但这一步骤不能估计线延时,因此和布线后的实际情况还有一定的差距,并不十分准确。目前的综合工具较为成熟,对于一般的设计可以省略这一步,但如果在布局布线后发现电路结构和设计意图不符,则需要回溯到综合后仿真来确认问题之所在。在功能仿真中介绍的软件工具一般都支持综合后仿真。第六节布局布线      布局布线可理解为利用实现工具把逻辑映射到目标器件结构的资源中,决定逻辑的最佳布局,选择逻辑与输入输出功能链接的布线通道进行连线,并产生相应文件(如配置文件与相关报告),实现是将综合生成的逻辑网表配置到具体的FPGA芯片上,布局布线是其中最重要的过程。布局将逻辑网表中的硬件原语和底层单元合理地配置到芯片内部的固有硬件结构上,并且往往需要在速度最优和面积最优之间作出选择。布线根据布局的拓扑结构,利用芯片内部的各种连线资源,合理正确地连接各个元件。目前,FPGA的结构非常复杂,特别是在有时序约束条件时,需要利用时序驱动的引擎进行布局布线。布线结束后,软件工具会自动生成报告,提供有关设计中各部分资源的使用情况。由于只有FPGA芯片生产商对芯片结构最为了解,所以布局布线必须选择芯片开发商提供的工具。7第七节:时序仿真      时序仿真,也称为后仿真,是指将布局布线的延时信息反标注到设计网表中来检测有无时序违规(即不满足时序约束条件或器件固有的时序规则,如建立时间、保持时间等)现象。时序仿真包含的延迟信息最全,也最精确,能较好地反映芯片的实际工作情况。由于不同芯片的内部延时不一样,不同的布局布线方案也给延时带来不同的影响。因此在布局布线后,通过对系统和各个模块进行时序仿真,分析其时序关系,估计系统性能,以及检查和消除竞争冒险是非常有必要的。在功能仿真中介绍的软件工具一般都支持综合后仿真。第八节:板级仿真与验证      板级仿真主要应用于高速电路设计中,对高速系统的信号完整性、电磁干扰等特征进行分析,一般都以第三方工具进行仿真和验证。第九节:芯片编程与调试      设计的最后一步就是芯片编程与调试。芯片编程是指产生使用的数据文件(位数据流文件,Bitstream Generation),然后将编程数据下载到FPGA芯片中。其中,芯片编程需要满足一定的条件,如编程电压、编程时序和编程算法等方面。逻辑分析仪(Logic Analyzer,LA)是FPGA设计的主要调试工具,但需要引出大量的测试管脚,且LA价格昂贵。目前,主流的FPGA芯片生产商都提供了内嵌的在线逻辑分析仪(如Xilinx ISE中的ChipScope、Altera QuartusII中的SignalTapII以及SignalProb)来解决上述矛盾,它们只需要占用芯片少量的逻辑资源,具有很高的实用价值。本书就介绍SIGNALTAP工具的使用。 此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2018-10-05
  • 发表了主题帖: VGA显示矩阵教学-至简设计

    三、模块设计架构设计我们要实现的功能,概括起来就是FPGA产生VGA时序,即控制VGA_R4~R0、VGA_G5~G0、VGA_B4~B0、VGA_HSYNC和VGA_VSYNC,让显示器显示红色。其中,VGA_HSYNC和VGA_VSYNC,FPGA可根据时序产生高低电平。而颜色数据,由于是固定的红色,FPGA也能自己产生,不需要外部输入图像的数据。那么我们的FPGA工程,可以定义输出信号hys表示行同步,用输出信号vys表示场同步,定义一个16位的信号lcd_rgb,其中lcd_rgb[15:11]表示VGA_R4~0,、lcd_rgb[10:5]表示VGA_G5~0,、lcd_rgb[4:0]表示VGA_B4~0。我们还需要时钟信号和复位信号来进行工程控制。综上所述,我们这个工程需要五个信号,时钟clk,复位rst_n,场同步信号vys、行同步信号hys和RGB输出信号lcd_rgb。 需要注意的是,输入进来的时钟clk是50MHz,而从分辨率参数表可知道,行单位的基准时钟是25 MHz。为此我们需要根据50MHz来产生一个25 MHz的时钟,然后再用于产生VGA时序。为了得到这个25M时钟,我们需要一个PLL。PLL可以认为是FPGA内的一个硬核,它的功能是根据输入的时钟,产生一个或多个倍频和分频后的输出时钟,同时可以调整这些输出时钟的相位、占空比等。例如,输入进来是50M时钟,如果我需要一个100M时钟,那么从逻辑上、代码上是不可能产生的,我们就必须用到PLL来产生了。整个工程的结构图如下。http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png PLL的生成方式过程,请看本案例的综合工程和上板一节的内容。 VGA驱动模块设计我们先分析功能。要控制显示器,让其产生红色,也就是让FPGA控制VGA_R0~4、VGA_G0~5、VGA_B0~4、VGA_VSYNC和VGA_HSYNC信号。那么VGA驱动模块,可以定义输出信号hys表示行同步,用输出信号vys表示场同步,定义一个16位的信号lcd_rgb,其中lcd_rgb[15:11]表示VGA_R4~0,、lcd_rgb[10:5]表示VGA_G5~0,、lcd_rgb[4:0]表示VGA_B4~0。同时该模块的工作时钟为25M,同时需要一个复位信号。综上所述,我们这个模块需要五个信号,25M时钟clk,复位rst_n,场同步信号vys、行同步信号hys和RGB输出信号lcd_rgb。 此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2018-10-04
  • 发表了主题帖: 手把手教你设计VGA显示颜色

    VGA显示颜色 一、项目背景 VGA介绍 VGA(Video Graphics Array)即视频图形阵列,是IBM在1987年随PS/2(PS/2 原是“Personal System 2”的意思,“个人系统2”,是IBM公司在1987年推出的一种个人电脑。PS/2电脑上使用的键盘鼠标接口就是现在的PS/2接口。因为标准不开放,PS/2电脑在市场中失败了。只有PS/2接口一直沿用到今天)一起推出的使用模拟信号的一种视频传输标准,在当时具有分辨率高、显示速率快、颜色丰富等优点,在彩色显示器领域得到了广泛的应用。这个标准对于现今的个人电脑市场已经十分过时。即使如此,VGA仍然是最多制造商所共同支持的一个标准,个人电脑在加载自己的独特驱动程序之前,都必须支持VGA的标准。例如,微软Windows系列产品的开机画面仍然使用VGA显示模式,这也说明其在显示标准中的重要性和兼容性。 VGA技术的应用还主要基于VGA显示卡的计算机、笔记本等设备。对于一些嵌入式VGA显示系统,可以在不使用VGA显示卡和计算机的情况下,实现VGA图像的显示和控制。系统具有成本低、结构简单、应用灵活的优点,可广泛应用于超市、车站、飞机场等公共场所的广告宣传和提示信息显示,也可应用于工厂车间生产过程中的操作信息显示,还能以多媒体形式应用于日常生活。 VGA管脚 具体实现操作方法下载附件 此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2018-09-30
  • 发表了主题帖: FPGA数码管动态扫描附件详细的讲解

    秒表功能 一、项目背景 同上一个项目。 二、设计目标 开发板或者模块是有 8 位数码管,本次设计需要使用1个数码管,即数码管0,实现类似于秒表的功能,具体要求如下: 复位后,数码管0显示数字0并持续1秒;然后显示数字1并持续2秒;然后显示数字2并持续3秒;以此类推,最后是显示数字9并持续10秒。然后再次循环 上板效果图如下图所示。     上板的演示效果,请登陆网址查看:www.mdy-edu.com/xxxx。 三、模块设计 我们要实现的功能,概括起来就是控制8个数码管,其中数码管0亮,其他数码管不亮。并让数码管0显示不同的数字。 要控制8个数码管,就需要控制位选信号,即FPGA要输出一个8位的位选信号,设为seg_sel,其中seg_sel[0]对应数码管0,seg_sel[1]对应数码管1,以此类推,seg_sel[7]对应数码管7。 要显示不同的数字,就需要控制段选信号,不需要用到DP,一共有7根线,即FPGA要输出一个7位的段选信号,设为seg_ment,seg_ment[6]~segm_ment[0]分别对应数码管的abcdefg(注意对应顺序)。 我们还需要时钟信号和复位信号来进行工程控制。 综上所述,我们这个工程需要4个信号,时钟clk,复位rst_n,输出的位选信号seg_sel和输出的段选信号seg_ment。 信号线        信号线        FPGA管脚        内部信号 SEG_E        SEG0        Y6        seg_ment[2] SEG_DP        SEG1        W6        未用到 SEG_G        SEG2        Y7        seg_ment[0] SEG_F        SEG3        W7        seg_ment[1] SEG_D        SEG4        P3        seg_ment[3] SEG_C        SEG5        P4        seg_ment[4] SEG_B        SEG6        R5        seg_ment[5] SEG_A        SEG7        T3        seg_ment[6] DIG1        DIG_EN1        T4        seg_sel[0] DIG2        DIG_EN2        V4        seg_sel[1] DIG3        DIG_EN3        V3        seg_sel[2] DIG4        DIG_EN4        Y3        seg_sel[3] DIG5        DIG_EN5        Y8        seg_sel[4] DIG6        DIG_EN6        W8        seg_sel[5] DIG7        DIG_EN7        W10        seg_sel[6] DIG8        DIG_EN8        Y10        seg_sel[7] 我们先分析要实现的功能,数码管0显示数字0,翻译成信号就是seg_sel的值为8’b1111_1110,seg_ment的值为7’b000_0001。然后数码管0显示数字1,也就是说seg_sel的值为8’b1111_1110,seg_ment的值为7’b100_1111。以此类推,数码管0显示数字9,就是seg_sel的值为8’b1111_1110,seg_ment的值为7’b000_0100。 seg_sel一直为8’hfe,不变化。seg_ment隔一段时间后会变化,而这个时间在不同时期还不相同。把时间信息补充上,得到下面的波形示意图。          上图就是seg_sel和seg_seg信号的变化波形图。在显示第1个时,seg_sel=8’hfe,seg_ment=7’h01并持续1秒;在第2个时,seg_sel=8’hfe,seg_ment=7’h4f并持续2秒;以此类推,第8个时,seg_sel=8’hfe,seg_ment=7’h04并持续10秒。然后又再次重复。 由波形图可知,我们需要1个计数器用来计算时间,如2秒、3秒等。另外,我们还需要一个计数器,用来计算在第几个阶段中。所以总共需要2个计数器。 本工程的工作时钟是50MHz,即周期为20ns,计数器计数到2_000_000_000/20=100_000_000个,我们就能知道2秒时间到了。以类类推,在第2次时,数到150_000_000个,就知道了3秒时间到。第9次时,数到500_000_000个,就表示10秒时间到。另外,由于该计数器是不停地计数,永远不停止的,可以认为加1条件一直有效,可写成:assign add_cnt0==1。综上所述,结合变量法,该计数器的代码如下 1 2 3 4 5 6 7 8 9 10 11 12 13 14        always @(posedge clk or negedge rst_n)begin     if(!rst_n)begin         cnt0 <= 0;     end     else if(add_cnt0)begin         if(end_cnt0)             cnt0 <= 0;         else             cnt0 <= cnt0 + 1;     end end assign add_cnt0 = 1 ; assign end_cnt0 = add_cnt0 && cnt0== x-1 ; 第二个计数器用于表示第几个,很自然可以看到,每个阶段完成后,该计数器加1,因此加1条件可为end_cnt0。该计数器一共要数10次。所以代码为: 1 2 3 4 5 6 7 8 9 10 11 12 13 14        always @(posedge clk or negedge rst_n)begin     if(!rst_n)begin         cnt1 <= 0;     end     else if(add_cnt1)begin         if(end_cnt1)             cnt1 <= 0;         else             cnt1 <= cnt1 + 1;     end end assign add_cnt1 = end_cnt0; assign end_cnt1 = add_cnt1 && cnt1== 10-1 ; 接下来设计seg_sel。该信号一直为8’hfe,所以代码直接写成如下: 1        assign seg_sel = 8'hfe ; 我们来思考输出信号seg_ment的变化。概括起来,在第1次的时候输出值为7’h01;在第2次的时候输出值为7’h4f;以此类推,在第8次的时候输出值为7’h0f。我们用信号cnt1来代替第几次,也就是:当cnt1==0的时候,输出值为7’h01;在cnt1==1的时候输出值为7’h4f;以此类推,在cnt1==9的时候输出值为7’h04。再进一步翻译成代码,就变成如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35        always  @(posedge clk or negedge rst_n)begin     if(rst_n==1'b0)begin         seg_ment <= 7'h01;     end     else if(cnt1==0)begin         seg_ment <= 7'h01;     end     else if(cnt1==1)begin         seg_ment <= 7'h4f;     end     else if(cnt1==2)begin         seg_ment <= 7'h12;     end     else if(cnt1==3)begin         seg_ment <= 7'h06;     end     else if(cnt1==4)begin         seg_ment <= 7'h4c;     end     else if(cnt1==5)begin         seg_ment <= 7'h24;     end     else if(cnt1==6)begin         seg_ment <= 7'h20;     end     else if(cnt1==7)begin         seg_ment <= 7'h0f;     end     else if(cnt1==8)begin         seg_ment <= 7'h00;     end     else if(cnt1==9)begin         seg_ment <= 7'h04;     end end 然后,用组合逻辑把x的值确定下来。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32        always  @(*)begin     if(cnt1==0)begin         x = 50_000_000;     end     else if(cnt1==1)begin         x = 100_000_000;     end     else if(cnt1==2)begin         x = 150_000_000;     end     else if(cnt1==3)begin         x = 200_000_000;     end     else if(cnt1==4)begin         x = 250_000_000;     end     else if(cnt1==5)begin         x = 300_000_000;     end     else if(cnt1==6)begin         x = 350_000_000;     end     else if(cnt1==7)begin         x = 400_000_000;     end     else if(cnt1==8)begin         x = 450_000_000;     end     else if(cnt1==9)begin         x = 500_000_000;     end end 此次,主体程序已经完成。接下来是将module补充完整。 将module的名称定义为my_time。并且我们已经知道该模块有4个信号:clk、rst_n、seg_sel和seg_ment,代码如下: 1 2 3 4 5 6        module my_time(     clk    ,     rst_n  ,     seg_sel,     seg_ment     ); 其中clk、rst_n是1位的输入信号,seg_sel是8位的输出信号,seg_ment是7位的输出信号,根据此,补充输入输出端口定义。代码如下: 1 2 3 4        input           clk       ; input           rst_n     ; output   [7:0]  seg_sel   ; output   [6:0]  seg_ment  ; 接下来定义信号类型。 cnt0是用always产生的信号,因此类型为reg。cnt0计数的最大值为500_000_000,需要用29根线表示,即位宽是29位。add_cnt0和end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下: 1 2 3        reg    [28:0]            cnt0        ; wire                     add_cnt0    ; wire                     end_cnt0    ; cnt1是用always产生的信号,因此类型为reg。cnt1计数的最大值为9,需要用4根线表示,即位宽是4位。add_cnt1和end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下: 1 2 3        reg    [3:0]            cnt1        ; wire                     add_cnt1    ; wire                     end_cnt1    ; seg_sel是用assign方式设计的,因此类型为wire,其一共有8根线,即位宽为8。因此代码如下: 1        wire    [7:0]  seg_sel   ; seg_ ment是用always方式设计的,因此类型为reg,其一共有7根线,即位宽为7。因此代码如下: 1        reg   [6:0]  seg_ment   ; x是用always方式设计的,因此类型为reg,他的位数和cnt0是一致的。 1        reg   [28:0]  x   ; 至此,整个代码的设计工作已经完成。整体代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93          module miaobiao(     clk    ,     rst_n  ,     seg_sel,     seg_ment     );     input               clk    ;     input               rst_n  ;     output    [7:0]  seg_sel   ;     output   [6:0]  seg_ment   ;     reg           [28:0]      cnt0;     reg           [3:0]       cnt1;     wire      add_cnt0;     wire      end_cnt0;     wire      add_cnt1;     wire      end_cnt1;     wire    [7:0]  seg_sel   ;     reg   [6:0]  seg_ment   ;     always @(posedge clk or negedge rst_n)begin         if(!rst_n)begin             cnt0 <= 0;         end         else if(add_cnt0)begin             if(end_cnt0)                 cnt0 <= 0;             else                 cnt0 <= cnt0 + 1;         end     end     assign add_cnt0 = 1;     assign end_cnt0 = add_cnt0 && cnt0== 50000000-1;     always @(posedge clk or negedge rst_n)begin         if(!rst_n)begin             cnt1 <= 0;         end         else if(add_cnt1)begin             if(end_cnt1)                 cnt1 <= 0;             else                 cnt1 <= cnt1 + 1;         end     end     assign add_cnt1 = end_cnt0;     assign end_cnt1 = add_cnt1 && cnt1==10-1 ;     assign seg_sel  = 8'hfe;   always  @(posedge clk or negedge rst_n)begin     if(rst_n==1'b0)begin         seg_ment <= 7'h01;     end     else if(cnt1==0)begin         seg_ment <= 7'h01;     end     else if(cnt1==1)begin         seg_ment <= 7'h4f;     end     else if(cnt1==2)begin         seg_ment <= 7'h12;     end     else if(cnt1==3)begin         seg_ment <= 7'h06;     end     else if(cnt1==4)begin         seg_ment <= 7'h4c;     end     else if(cnt1==5)begin         seg_ment <= 7'h24;     end     else if(cnt1==6)begin         seg_ment <= 7'h20;     end     else if(cnt1==7)begin         seg_ment <= 7'h0f;     end     else if(cnt1==8)begin         seg_ment <= 7'h00;     end     else if(cnt1==9)begin         seg_ment <= 7'h04;     end end always  @(*)begin     if(cnt1==0)begin         x = 50_000_000;     end     else if(cnt1==1)begin         x = 100_000_000;     end     else if(cnt1==2)begin         x = 150_000_000;     end     else if(cnt1==3)begin         x = 200_000_000;     end     else if(cnt1==4)begin         x = 250_000_000;     end     else if(cnt1==5)begin         x = 300_000_000;     end     else if(cnt1==6)begin         x = 350_000_000;     end     else if(cnt1==7)begin         x = 400_000_000;     end     else if(cnt1==8)begin         x = 450_000_000;     end     else if(cnt1==9)begin         x = 500_000_000;     end end   endmodule 下一步是新建工程和上板查看现象。 四、综合工程和上板 新建工程 首先在d盘中创建名为“miaobiao”的工程文件夹,将写的代码命名为“miaobiao.v”,顶层模块名为“miaobiao”。 然后打开Quartus Ⅱ,点击File下拉列表中的New Project Wzard...新建工程选项。 3.再出现的界面中直接点击Next。 4.之后出现的是工程文件夹、工程名、顶层模块名设置界面。按照之前的命名进行填写,第一栏选择工程文件夹“miaobiao”,第二栏选择工程文件“miaobiao”,最后一栏选择顶层模块名“miaobiao”,然后点击”Next”,在出现的界面选择empty project。    5.之后是文件添加界面。添加之前写的“miaobian.v”文件,点击右侧的“Add”按钮,之后文件还会出现在大方框里,之后点击“Next”。 器件型号选择界面。选择Cyclone ⅣE,在芯片型号选择处选择EP4CE15F23C8,然后点击“Next”。 EDA工具界面。直接点击“Next”。 8.之后出现的界面是我们前面设置的总结,确认无误后点击“Finish”。 综合 1.新建工程步骤完成后,就会出现以下界面。在“Project Navigator”下选中要编译的文件,点击上方工具栏中“Start Compilation”编译按钮(蓝色三角形)。 2.编译成功后会出现一下界面。 配置管脚 1.点击箭头所指的管脚配置按钮“Pin Planner”,进入管脚配置界面。 2.在下图location处双击填上对应的管脚号,管脚号可参照整体代码下方的管脚配置,回车即可。 管脚配置: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1         set_location_assignment PIN_G1 -to clk set_location_assignment PIN_AB12 -to rst_n set_location_assignment PIN_T3 -to seg_ment[6] set_location_assignment PIN_R5 -to seg_ment[5] set_location_assignment PIN_P4 -to seg_ment[4] set_location_assignment PIN_P3 -to seg_ment[3] set_location_assignment PIN_Y6 -to seg_ment[2] set_location_assignment PIN_W7 -to seg_ment[1] set_location_assignment PIN_Y7 -to seg_ment[0] set_location_assignment PIN_Y10 -to seg_sel[7] set_location_assignment PIN_W10 -to seg_sel[6] set_location_assignment PIN_W8 -to seg_sel[5] set_location_assignment PIN_Y8 -to seg_sel[4] set_location_assignment PIN_Y3 -to seg_sel[3] set_location_assignment PIN_V3 -to seg_sel[2] set_location_assignment PIN_V4 -to seg_sel[1] set_location_assignment PIN_T4 -to seg_sel[0] 布局布线 管脚配置完成后,在进行一次编译。 连接开发板 图中,下载器接入电脑USB接口,电源接入电源,然后摁下蓝色开关。 上板 1.双击Tasks一栏中”Program Device”。 2.会出现如下界面,点击add file添加.sof文件,点击“Start”,会在“Progress”出显示进度。 3.进度条中提示成功后,即可在开发板上观察到相应的现象。

  • 2018-09-29
  • 发表了主题帖: FPGA数码管动态扫描附件详细的讲解

    数码管动态扫描 一、项目背景led数码管(LED Segment Displays)是由多个发光二极管封装在一起组成“8”字型的器件,引线已在内部连接完成,只引出它们的各个笔划,公共电极。led数码管常用段数一般为7段,如上图中的abcdefg,有的还会有一个小数点,如图中的h。http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png数码管要正常显示,就要用驱动电路来驱动数码管的各个段码,从而显示出我们要的数字。按发光二极管单元连接方式可分为共阳极数码管和共阴极数码管。共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,共阳数码管在应用时应将公共极COM接到+5V,当某一字段发光二极管的阴极为低电平时,相应字段就点亮,当某一字段的阴极为高电平时,相应字段就不亮。共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码管,共阴数码管在应用时应将公共极COM接到地线GND上,当某一字段发光二极管的阳极为高电平时,相应字段就点亮,当某一字段的阳极为低电平时,相应字段就不亮。下表列出了要显示的数字,以及对应的abcdefg的值。  显示  数字  共阳abcdefg  2进制共阳abcdefg  16进制共阴abcdefg  2进制共阴abcdefg  16进制07’b00000017’h017’b 11111107’h7e17’b 10011117’h4f7’b 01100007’h3027’b 00100107’h127’b 11011017’h6d37’b 00001107’h067’b 11110017’h7947’b 10011007’h4c7’b 01100117’h3357’b 01001007’h247’b 10110117’h5b67’b 01000007’h207’b 10111117’h3f77’b 00011117’h0f7’b 11100007’h7087’b 00000007’h007’b 11111117’h7f97’b 00001007’h047’b 11110117’h7b 例如,共阳数码管中,abcdefg的值分别是1001111时,也就是b和c字段亮,其他字段不亮,这时就显示了数字“1”。如果要显示多个数码管,根据数码管的驱动方式的不同,可以分为静态式和动态式两类。静态驱动也称直流驱动。静态驱动是指每个数码管的每一个段码都由一个单片机的I/O端口进行驱动,或者使用如BCD码二-十进制译码器译码进行驱动。静态驱动的优点是编程简单,显示亮度高,缺点是占用I/O端口多,如驱动5个数码管静态显示则需要5×8=40根I/O端口来驱动,要知道一个89S51单片机可用的I/O端口才32个,实际应用时必须增加译码驱动器进行驱动,增加了硬件电路的复杂性。数码管动态显示接口是应用最为广泛的一种显示方式之一,动态驱动是将所有数码管的8个显示笔划"a,b,c,d,e,f,g,dp"的同名端连在一起,另外为每个数码管的公共极COM增加位选通控制电路,位选通由各自独立的I/O线控制,当要输出字形码时,所有数码管都接收到相同的字形码,但究竟是哪个数码管会显示出字形,取决于单片机对位选通COM端电路的控制,所以我们只要将需要显示的数码管的选通控制打开,该位就显示出字形,没有选通的数码管就不会亮。通过分时轮流控制各个数码管的的COM端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的I/O端口,而且功耗更低。明德扬开发板上一共有2组4位的共阳数码管,也就是说一共有8个共阳数码管。数码管的配置电路如下。http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg图中的SEG_A,SEG_B~SEG_DP,是段选信号,这些信号都是8个数码管共用的。DIG1~DIG8是位选信号,分别对应8个数码管。对应的位选信号为0,就表示将段选信号的值赋给该数码管。例如DIG3为0,表示将段选信号SEG_A~SEG_DP的值赋给数码管3。SEG_A~SEG_DP,DIG1~DIG8,都是连接到电阻,如下图。http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image006.jpg由此可见,SEG_A~SEG_DP是由SEG0~SEG7产生的,DIG1~DIG8是由DIG_EN1~DIG_EN8产生的。 而SEG0~SEG7和DIG_EN1~DIG_EN8直接连到FPGA的IO上。 http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image007.png 这些信号与FPGA管脚的对应关系如下表。  信号线  信号线FPGA管脚SEG_ESEG0Y6SEG_DPSEG1W6SEG_GSEG2Y7SEG_FSEG3W7SEG_DSEG4P3SEG_CSEG5P4SEG_BSEG6R5SEG_ASEG7T3DIG1DIG_EN1T4DIG2DIG_EN2V4DIG3DIG_EN3V3DIG4DIG_EN4Y3DIG5DIG_EN5Y8DIG6DIG_EN6W8DIG7DIG_EN7W10DIG8DIG_EN8Y10 也就是说,FPGA通过控制上面中的管脚,就控制了数码管的显示。 二、设计目标开发板或者模块是有 8 位数码管,本次设计需要使用 8 个数码管,实现数码管显示功能,具体要求如下:复位后,数码管 0 显示数字 0;1 秒后,轮到数码管 1 显示数字 1;1 秒后,轮到数码管 2 显示数字 2;以此类推,每隔 1 秒变化,最后是数码管 7 显示数字 7。然后再次循环。 上板效果图如下图所示。上板的演示效果,请登陆网址查看:www.mdy-edu.com/xxxx。 http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image009.jpghttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image011.jpghttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image013.jpghttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image015.jpghttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image017.jpghttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image019.jpghttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image021.jpghttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image023.jpg三、模块设计我们要实现的功能,概括起来就是控制8个数码管,让数码管显示不同的数字。要控制8个数码管,就需要控制位选信号,即FPGA要输出一个8位的位选信号,设为seg_sel,其中seg_sel[0]对应数码管0,seg_sel[1]对应数码管1,以此类推,seg_sel[7]对应数码管7。要显示不同的数字,就需要控制段选信号,不需要用到DP,一共有7根线,即FPGA要输出一个7位的段选信号,设为seg_ment,seg_ment[6]~segm_ment[0]分别对应数码管的abcdefg(注意对应顺序)。我们还需要时钟信号和复位信号来进行工程控制。综上所述,我们这个工程需要4个信号,时钟clk,复位rst_n,输出的位选信号seg_sel和输出的段选信号seg_ment。其中,seg_sel和seg_ment的对应关系下如下:   信号线  信号线FPGA管脚内部信号SEG_ESEG0Y6seg_ment[2]SEG_DPSEG1W6未用到SEG_GSEG2Y7seg_ment[0]SEG_FSEG3W7seg_ment[1]SEG_DSEG4P3seg_ment[3]SEG_CSEG5P4seg_ment[4]SEG_BSEG6R5seg_ment[5]SEG_ASEG7T3seg_ment[6]DIG1DIG_EN1T4seg_sel[0]DIG2DIG_EN2V4seg_sel[1]DIG3DIG_EN3V3seg_sel[2]DIG4DIG_EN4Y3seg_sel[3]DIG5DIG_EN5Y8seg_sel[4]DIG6DIG_EN6W8seg_sel[5]DIG7DIG_EN7W10seg_sel[6]DIG8DIG_EN8Y10seg_sel[7] 我们先分析要实现的功能,数码管0显示数字0,翻译成信号就是seg_sel的值为8’b1111_1110,seg_ment的值为7’b000_0001。数码管1显示数字1,也就是说seg_sel的值为8’b1111_1101,seg_ment的值为7’b100_1111。以此类推,数码管7显示数字7,就是seg_sel的值为8’b0111_1111,seg_ment的值为7’b000_1111。再留意下,以上都是每隔1秒进行变化,并且是8个数码管轮流显示,那么波形示意图如下图所示。 http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image025.png上图就是seg_sel和seg_seg信号的变化波形图。在显示第1个时,seg_sel=8’hfe,seg_ment=7’h01并持续1秒;在第1个时,seg_sel=8’hfd,seg_ment=7’h4f并持续1秒;以此类推,第8个时,seg_sel=8’h7f,seg_ment=7’h0f并持续1秒。然后又再次重复。由波形图可知,我们需要1个计数器用来计算1秒的时间。本工程的工作时钟是50MHz,即周期为20ns,计数器计数到1_000_000_000/20=50_000_000个,我们就能知道1秒时间到了。另外,由于该计数器是不停地计数,永远不停止的,可以认为加1条件一直有效,可写成:assignadd_cnt==1。综上所述,该计数器的代码如下。http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image027.jpg再次观察波形图,我们发现有第1个,第2个直到第8个,说明这还需要另外一个计数器来表示第几个。该计数器表示第几个,自然是完成1秒就加1,因为加1条件可为end_cnt0。该计数器一共要数8次。所以代码为:http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image029.jpg有了两个计数器,我们来思考输出信号seg_sel的变化。概括起来,在第1次的时候输出值为8’hfe;在第2次的时候输出值为8’hfd;以此类推,在第8次的时候输出值为8’h7f。我们用信号cnt1来代替第几次,也就是:当cnt1==0的时候,输出值为8’hfe;在cnt1==1的时候输出值为8’hfd;以此类推,在cnt1==7的时候输出值为8’h7f。再进一步翻译成代码,就变成如下:http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image030.png读者有没有发现,上面代码基本上和文字描述是一模一样的,这进一步展现了verilog是“硬件描述语言”。上面的代码是能正确实现seg_sel功能的,从实现角度和资源角度来说,都挺好。但代码进一步概括,可以化简如下:http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image031.png对上面代码解释一下,第131行是指先将8’b1向左移位,再取反后的值,赋给seg_sel。假设此时cnt1等于0,那么8’b1<<0的结果是8’b0000_0001,取反的值为8’hfe;假设cnt1等于3,那么8’b1<<3的结果为8’b000_1000,取反后的结果为8’b1111_0111,即8’hf7。与第一种写法的结果都是相同的。 我们来思考输出信号seg_ment的变化。概括起来,在第1次的时候输出值为7’h01;在第2次的时候输出值为7’h4f;以此类推,在第8次的时候输出值为7’h0f。我们用信号cnt1来代替第几次,也就是:当cnt1==0的时候,输出值为7’h01;在cnt1==1的时候输出值为7’h4f;以此类推,在cnt1==7的时候输出值为7’h0f。再进一步翻译成代码,就变成如下:http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image032.png上面的代码正确地实现了seg_ment的功能,对于本工程说已经完美。但我们分析一下,就知道上面代码实现了类似译码的功能,将数字设成数码管显示的值,代码里只对0~7进行译码。很自然的,我先做一个通用的译码模块,将0~9都进行译码,以后就方便调用了。例如改成下面代码。http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image033.png 然后我们只要控制好data就能实现想要在数码管显示的数字,如下面代码。http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image034.png当cnt1=0,则数码管会显示0。当cnt1=1,则数码管会显示1。在代码的最后一行写下endmodulehttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image035.png      至此,主体程序已经完成。接下来是将module补充完整。将module的名称定义为my_seg。并且我们已经知道该模块有4个信号:clk、rst_n、seg_sel和seg_ment,代码如下:http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image036.png 其中clk、rst_n是1位的输入信号,seg_sel是8位的输出信号,seg_ment是7位的输出信号,根据此,补充输入输出端口定义。代码如下:http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image037.png 接下来定义信号类型。cnt0是用always产生的信号,因此类型为reg。cnt0计数的最大值为50_000_000,需要用26根线表示,即位宽是26位。add_cnt0和end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image038.pngcnt1是用always产生的信号,因此类型为reg。cnt1计数的最大值为7,需要用3根线表示,即位宽是3位。add_cnt1和end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下:http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image039.pngseg_sel是用always方式设计的,因此类型为reg,其一共有8根线,即位宽为8。因此代码如下:http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image040.pngseg_ ment是用always方式设计的,因此类型为reg,其一共有7根线,即位宽为7。因此代码如下:http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image041.png 如果做了译码电路,即用到了data这个信号。那么data是用assign设计的,所以类型为wire,其最大值为9,所以需要4位位宽。代码如下:http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image042.png 至此,整个代码的设计工作已经完成。下一步是新建工程和上板查看现象。四、综合工程和上板新建工程(1.)点击File 在File菜单中选择New Project Wizard.... http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image044.jpg2.弹出Introduction界面选择nexthttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image046.jpg (2.)工程文件夹,工程名,顶层模块名设置界面点击next之后进入此界面按如下路径建立文件夹E:/quartus_project/project_seg点击“选择工程文件夹(what is theworking directory for this project?)”后面的http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image047.png键 找到之前建立的立文件夹E:/quartus_project/project_seg将工程名命名栏(what is the nameof this project)中输入“my_seg”在顶层模块命名栏(what is the nameof the top-level design entity for this project?This name is case sensitive andmust exactly match the entity name in the design file)中输入“my_seg”5.命名完毕后点击nexthttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image049.jpg(3.)文件添加界面1.从上一界面进入此界面之后点击http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image047.png浏览文件夹http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image051.jpg2.找到我们之前写的.v文件 之后选中它并点击打开http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image053.jpg之后点击add添加此.v文件http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image055.jpg(4.)器件选择界面1.在Device family这一项之中选择 Cyclone IV E2.在下部的Available device 选择EP4CE6F17C8这一项完成后点击nexthttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image057.jpgEDA工具界面(采用默认配置即可)点击nexthttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image059.jpg 总结界面直接点击next即可http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image061.jpg综合1.点击http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image062.png此键进行编译http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image064.jpg之后等待编译成功 点击okhttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image065.png配置管脚编译成功后配置管脚点击图中位置(pin planer键)进入管脚配置界面http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image067.jpg在下图红框区域配置管脚http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image069.jpg按下图内容进行管脚配置  信号线  信号线FPGA管脚内部信号SEG_ESEG0Y6seg_ment[2]SEG_DPSEG1W6未用到SEG_GSEG2Y7seg_ment[0]SEG_FSEG3W7seg_ment[1]SEG_DSEG4P3seg_ment[3]SEG_CSEG5P4seg_ment[4]SEG_BSEG6R5seg_ment[5]SEG_ASEG7T3seg_ment[6]DIG1DIG_EN1T4seg_sel[0]DIG2DIG_EN2V4seg_sel[1]DIG3DIG_EN3V3seg_sel[2]DIG4DIG_EN4Y3seg_sel[3]DIG5DIG_EN5Y8seg_sel[4]DIG6DIG_EN6W8seg_sel[5]DIG7DIG_EN7W10seg_sel[6]DIG8DIG_EN8Y10seg_sel[7] 布局布线管脚配置完毕后再次点击http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image062.png此键进行编译http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image071.jpg连接开发板http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image073.jpghttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image075.jpg上板右键Program Device 选择Open 进入烧录界面http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image077.jpg 2.点击starthttp://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image078.png开始烧录程序http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image080.jpgProgress这一栏变为绿色显示百分之百successful即代表烧录成功http://file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image082.jpg 此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2018-09-26
  • 发表了主题帖: 基于FPGA至简设计法的4位闪烁灯 附件更详细

    4位闪烁灯一、项目背景LED灯的理论、教学板的原理图,已经在案例1位闪烁灯中有详细的描述,在此不再讲述,有兴趣的读者可以返回去阅读。 二、设计目标本工程使用4个LED灯---LED1~LED4,实现一个呼吸灯的功能。这4个灯具体的变化情况为:第1个灯隔1秒后,亮1秒;然后第2个灯隔1秒后,亮2秒;然后第3个灯隔1秒后,亮3秒,最后第4个灯隔1秒,亮4秒。如此循环往复。下面是波形图: 上板效果图如下图所示。 三、模块设计我们先分析一下板子上的LED灯。每个LED灯都有一个信号来控制,该信号为0,则灯亮,如果该信号为1,则灯来。现在我们要控制4个LED灯亮灭,那就需要4个信号,假设分别为led0、led1、led2和led3。这4个信号分别连接到4个led灯上。如果要让LED0灯0亮,LED1~3灯来,那FPGA就让led0信号为0,led1~3信号都为1。综上所述,我们这个工程需要6个信号:时钟clk,复位rst_n、led0、led1、led2和led3。我们再分析一下功能需求,第1个灯隔1秒后,亮1秒;然后第2个灯隔1秒后,亮2秒;然后第3个灯隔1秒后,亮3秒,最后第4个灯隔1秒,亮4秒。如此循环往复。上面的功能需求,也可以翻译成:对于LED0,复位后,先灭1秒,亮1秒,然后再灭12秒,循环往复;对于LED1,复位后,先灭3秒,亮2秒,然后再灭9秒,循环往复;对于LED2,复位后,先灭6秒,亮3秒,然后再灭5秒,循环往复;对于LED3,先灭10秒,亮4秒,循环往复。 再将其翻译成信号来理解:复位后,让信号led0=1并持续1秒,然后让led0=0并持续1秒,然后让led0=1持续12秒。循环往复。复位后,让信号led1=1并持续3秒,然后让led1=0并持续2秒,然后让led1=1持续9秒。循环往复。复位后,让信号led2=1并持续6秒,然后让led2=0并持续3秒,然后让led2=1持续5秒。循环往复。复位后,让信号led3=1并持续10秒,然后让led3=0并持续4秒。循环往复。 再将其翻译成波形如下图所示。 由图中可看到,信号led0~led3的变化单位最小是1秒,同时4个信号都是经过14秒后就循环一次。由至简设计法的思想,很容易就得出我们需要2个计数器,1个计数器用来计算1秒时间,另1个计数器用来计算14秒。有了这两个计数器,led0~led3的变化时间就有了标准。我们用1个计数器用来计算1秒时间,该计数器名称为cnt0。本工程的工作时钟是50MHz,即周期为20ns,计数器计数到1_000_000_000/20=50_000_000个,我们就能知道1秒时间到了。该计数器是不停地计数,永远不停止的,可以认为加1条件一直有效,可写成:assignadd_cnt==1。综上所述,该计数器的代码如下。 我们再用1个计数器用来表示14秒,名称为cnt1。该计数器表示次数,自然是每隔1秒就加1,那就是end_cnt0。该计数器一共要数14次。所以代码为: 有了两个计数器,我们来思考输出信号led0的变化。概括起来,led0有两种变化点:变0和变1。变0的原因都是计数到1秒时间,也就是add_cnt1 &&cnt1==1-1时,led0变0。变1的原因,则是数到2秒时间时,即add_cnt1 &&cnt1==2-1时,led0变1。所以led0信号的代码如下: 接下来我们思考输出信号led1的变化。概括起来,led1有两种变化点:变0和变1。变0的原因都是计数到3秒时间,也就是add_cnt1 &&cnt1==3-1时,led1变0。变1的原因,则是数到5秒时间时,即add_cnt1 &&cnt1==5-1时,led1变1。所以led1信号的代码如下: 接下来我们思考输出信号led2的变化。概括起来,led2有两种变化点:变0和变1。变0的原因都是计数到6秒时间,也就是add_cnt1 &&cnt1==6-1时,led2变0。变1的原因,则是数到9秒时间时,即add_cnt1 &&cnt1==9-1时,led2变1。所以led2信号的代码如下: 接下来我们思考输出信号led3的变化。概括起来,led3有两种变化点:变0和变1。变0的原因都是计数到10秒时间,也就是add_cnt1 &&cnt1==10-1时,led3变0。变1的原因,则是数到14秒时间时,即add_cnt1 &&cnt1==14-1,也就是end_cnt1时,led3变1。所以led3信号的代码如下: 此次,主体程序已经完成。接下来是将module补充完整。将module的名称定义为huxiled。并且我们已经知道该模块有六个信号:clk、rst_n、led0、led1、led2、led3。为此,代码如下: 其中clk、rst_n是输入信号,led0、led1、led2、led3是输出信号,并且六个信号都是1比特的,根据这些信息,我们补充输入输出端口定义。代码如下: 接下来定义信号类型。cnt0是用always产生的信号,因此类型为reg。cnt0计数的最大值为500_000_000,需要用29根线表示,即位宽是29位。因此代码如下: add_cnt0和end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下: cnt1是用always产生的信号,因此类型为reg。cnt1计数的最大值为8,需要用4根线表示,即位宽是4位。因此代码如下: add_cnt1和end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下: led0、led1、led2、led3是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下: 至此,整个代码的设计工作已经完成。下一步是新建工程和上板查看现象。四、综合工程和上板新建工程首先在d盘中创建名为“huxiled”的工程文件夹,将写的代码命名为“huxiled.v”,顶层模块名为“huxiled”。 然后打开Quartus Ⅱ,点击File下拉列表中的New Project Wzard...新建工程选项。 3.再出现的界面中直接点击Next。 4.之后出现的是工程文件夹、工程名、顶层模块名设置界面。按照之前的命名进行填写,然后点击Next。 5.之后是文件添加界面。点击红色箭头处,添加之前写的“huxiled.v”文件,点击右侧的“Add”按钮,之后文件还会出现在黑色箭头处,点击,之后点击“Next”。 器件型号选择界面。在上方红色箭头处选择CycloneⅣE,在中间红色箭头处选择EP4CE6F17C8,然后点击“Next”。 EDA工具界面。直接点击“Next”。 8.之后出现的界面,点击“Finish”。 综合1.新建工程步骤完成后,就会出现以下界面。选中要编译的文件,点击编译按钮。 2.编译成功后会出现一下界面,点击“OK”。 配置管脚1.点击箭头所指的管脚配置按钮。 2.下图是我们要用的原理图,其中箭头所指就是我们要配置的六个管脚,分别是clk、rst、led0、led1、led2、led3。 3.在下图箭头处双击填上对应的管脚号,回车即可。 布局布线管脚配置完成后,在进行一次编译。连接开发板图中,下载器接入电脑USB接口,电源接入电源,然后摁下下方蓝色开关。 上板1.双击箭头所指位置。 2.会出现如下界面,点击“Start”,会在“Progress”出显示进度。 3.进度条中提示成功后,即可在开发板上观察到相应的现象。

  • 2018-09-22
  • 发表了主题帖: 1位呼吸灯综合工程和上板【1241003385】

    此内容由EEWORLD论坛网友guyu_新建工程首先在d盘中创建名为“my_led”的工程文件夹,将写的代码命名为“my_led.v”,顶层模块名为“my_led”。                                然后打开Quartus Ⅱ,点击File下拉列表中的New Project Wzard...新建工程选项。 3.再出现的界面中直接点击Next。 4.之后出现的是工程文件夹、工程名、顶层模块名设置界面。按照之前的命名进行填写,然后点击Next。 5.之后是文件添加界面。点击红色箭头处,添加之前写的“my_led.v”文件,点击右侧的“Add”按钮,之后文件还会出现在黑色箭头处,点击,之后点击“Next”。 器件型号选择界面。在上方红色箭头处选择CycloneⅣE,在中间红色箭头处选择EP4CE6F17C8,然后点击“Next”。 EDA工具界面。直接点击“Next”。 8.之后出现的界面,点击“Finish”。 综合1.新建工程步骤完成后,就会出现以下界面。选中要编译的文件,点击编译按钮。 2.编译成功后会出现一下界面,点击“OK”。 配置管脚1.点击箭头所指的管脚配置按钮。 2.下图是我们要用的原理图,其中箭头所指就是我们要配置的三个管脚,分别是clk、rst、led0。  3.在下图箭头处双击填上对应的管脚号,回车即可。 布局布线管脚配置完成后,在进行一次编译。连接开发板图中,下载器接入电脑USB接口,电源接入电源,然后摁下下方蓝色开关。 上板1.双击箭头所指位置。 2.会出现如下界面,点击“Start”,会在“Progress”出显示进度。 3.进度条中提示成功后,即可在开发板上观察到相应的现象。1原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2018-09-21
  • 发表了主题帖: FPGA1位闪烁灯设计[1241003385]

    1位闪烁灯设计一、项目背景LED(Light Emitting Diode),发光二极管,是一种能够将电能转化为可见光的固态的半导体器件,它可以直接把电转化为光。LED的心脏是一个半导体的晶片,晶片的一端附在一个支架上,一端是负极,另一端连接电源的正极,使整个晶片被环氧树脂封装起来。半导体晶片由两部分组成,一部分是P型半导体,在它里面空穴占主导地位,另一端是N型半导体,在这边主要是电子。但这两种半导体连接起来的时候,它们之间就形成一个P-N结。当电流通过导线作用于这个晶片的时候,电子就会被推向P区,在P区里电子跟空穴复合,然后就会以光子的形式发出能量,这就是LED灯发光的原理。而光的波长也就是光的颜色,是由形成P-N结的材料决定的。LED可以直接发出红、黄、蓝、绿、青、橙、紫、白色的光。  最初LED用作仪器仪表的指示光源,后来各种光色的LED在交通信号灯和大面积显示屏中得到了广泛应用,产生了很好的经济效益和社会效益。以12英寸的红色交通信号灯为例,在美国本来是采用长寿命,低光视效能的140瓦白炽灯作为光源,它产生2000流明的白光。经红色滤光片后,光损失90%,只剩下200流明的红光。而在新设计的灯中,Lumileds公司采用了18个红色LED光源,包括电路损失在内,共耗电14瓦,即可产生同样的光效。汽车信号灯也是LED光源应用的重要领域。对于一般照明而言,人们更需要白色的光源。1998年发白光的LED开发成功。这种LED是将GaN芯片和钇铝石榴石(YAG)封装在一起做成。GaN芯片发蓝光(λp=465nm,Wd=30nm),高温烧结制成的含Ce3+的YAG荧光粉受此蓝光激发后发出黄色光射,峰值550nLED灯m。蓝光LED基片安装在碗形反射腔中,覆盖以混有YAG的树脂薄层,约200-500nm。 LED基片发出的蓝光部分被荧光粉吸收,另一部分蓝光与荧光粉发出的黄光混合,可以得到白光。对于InGaN/YAG白色LED,通过改变YAG荧光粉的化学组成和调节荧光粉层的厚度,可以获得色温3500-10000K的各色白光。这种通过蓝光LED得到白光的方法,构造简单、成本低廉、技术成熟度高,因此运用最多。 明德扬的教学板一共有8个可发绿光的LED灯。下面是LED灯的原理图。                              左边的LED6~LED13是板子的丝印。右边的LED1~LED8是信号线名,读者在板子上是不可见的。LED灯一端连着高电平3.3V,另一端是信号线LED1~LED8。如果LED1~LED8是高电平,则电流不导通,那么LED灯则不会发光。如果LED1~LED8是低电平,则电流会导通,那么LED灯就发光。所以LED灯发不发光,是取决于信号LED1~LED8是处于什么电平。 信号线LED1~LED8又连到哪里呢?搜索下原理图文档,可以发现这些信号是连到FPGA的管脚上的。 下面信号线和FPGA管脚的连接图,例如信号线LED1是连接到FPGA的AA4管脚上。   教学板丝印  信号线FPGA管脚LED1LED1AA4LED2LED2AB4LED3LED3AA5LED4LED4AB6LED5LED5AA10LED6LED6AB13LED7LED7AB14LED8LED8AB16 LED1~LED8分别与FPGA的8个管脚相连,所以LED1~LED8处于什么电平,即LED灯是否要发光,就取决于FPGA管脚的输出了。例如FPGA管脚AB14连到LED7上。要控制这个灯的亮灭,FPGA只需要将管脚AB14输出为低高就可以了。当输出为高电平时,LED7灯为灭,当输出为低电平时,LED7灯为暗。8个LED灯都可由FPGA独立控制。二、设计目标本工程使用1个LED灯---LED1,实现一个闪烁灯的功能。工程的工作时钟是50M,也就是时钟周期为20ns。当管脚AA4输出低电平时,LED1灯亮,输出高电平时,LED1灯灭。具体功能要求是:隔1秒,亮N秒。N的变化是:1,2,3,---,9秒,然后再次循环。下面是波形图: 上板效果图如下图所示。 上板的演示视频,请登陆网址查看:www.mdy-edu.com/xxxx。三、模块设计我们先分析一下板子上的LED灯。要控制1个LED灯亮和灭,那就FPGA需要产生一个信号,假定为led,这个信号连接到led灯上。要让LED灯灭,FPGA将信号led输出为1;要让LED灯亮,FPGA将信号led输出为0。综上所述,我们这个工程需要三个信号,时钟clk,复位rst_n和输出信号led。我们再分析一下功能需求,LED灯的变化规律是暗1秒,亮N秒,其中N的变化是:1,2,3,---,9秒,然后再次循环。从现象转化成信号,其实就是信号led=1持续1秒,然后led=0持续N秒,其中N的变化是:1,2,3,---,9秒。波形示意图如下: 上图就是led信号的变化波形图。在第1次时,led=1并持续1秒,然后led=0并持续1秒,共2秒时间;在第2次时,led=1并持续1秒,然后led=0并持续2秒,共3秒时间;以此类推,第9次时,led=1并持续1秒,然后led=0并持续9秒,共10秒时间。然后又再次重复。由波形图可知,我们需要1个计数器用来计算时间,如2秒、3秒等。本工程的工作时钟是50MHz,即周期为20ns,计数器计数到2_000_000_000/20=100_000_000个,我们就能知道2秒时间到了。以此类推,在第2次时,数到150_000_000个,就知道了3秒时间到。第9次时,数到500_000_000个,就表示10秒时间到。另外,由于该计数器是不停地计数,永远不停止的,可以认为加1条件一直有效,可写成:assignadd_cnt==1。综上所述,结合变量法,该计数器的代码如下。 其中x表示该计数器cnt0要数的个数。该值如何定义,后面再思考。再次观察波形图,我们发现有第1次,第2次直到第9次的字,说明这还需要另外一个计数器来表示第几次。该计数器表示次数,自然是一次完成了就加1,因为加1条件可为end_cnt0。该计数器一共要数9次。所以代码为: 有了两个计数器,我们来思考输出信号led的变化。概括起来,led有两种变化点:变0和变1。变0的原因都是计数到1秒时间,也就是cnt0数到1_000_000_000/20=50_000_000个时,led变0。变1的原因,都是计数时间到了,即end_cnt0。所以led信号的代码如下:                最后我们再来思考变量x,我们在讨论计数器cnt0的时候,曾经说过“计数器计数到2_000_000_000/20=100_000_000个,我们就能知道2秒时间到了。以类类推,在第2次时,数到150_000_000个,就知道了3秒时间到。第9次时,数到500_000_000个,就表示10秒时间到。”可以看到,cnt0要数多少个是跟第几次有关系的。第1次,数100_000_000个,第2次数150_000_000个。也就是与cnt1有关。因此x的代码如下:                            此次,主体程序已经完成。接下来是将module补充完整。将module的名称定义为my_led。并且我们已经知道该模块有三个信号:clk、rst_n和led。为此,代码如下: 其中clk、rst_n是输入信号,led是输出信号,并且三个信号都是1比特的,根据这些信息,我们补充输入输出端口定义。代码如下: 接下来定义信号类型。cnt0是用always产生的信号,因此类型为reg。cnt0计数的最大值为500_000_000,需要用29根线表示,即位宽是29位。因此代码如下: add_cnt0和end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下: cnt1是用always产生的信号,因此类型为reg。cnt1计数的最大值为8,需要用4根线表示,即位宽是4位。因此代码如下: add_cnt1和end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下: led是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下: x是用always方式设计的,因此类型为reg。并且其值是最大是500_000_000,需要29根线表示即可。因此代码如下: 至此,整个代码的设计工作已经完成。下一步是新建工程和上板查看现象。

  • 2018-09-20
  • 发表了主题帖: FPGA至简设计法高效设计[1241003385]

    至简设计法高效设计上一节我们描述了明德扬的通用设计方法。在阐述案例过程中,我们画出了大量的波形图。有读者可能会问,在工作中,我们是不是也需要先大量地画波形图,再来写代码呢?不是的!工作中,我们要设计的系统更加的复杂,一个模块的信号也非常地多,如果我们每个模块都要画波形图,这不是明德扬提倡的至简设计。何况,信号一天,画出来的波形信号也是相当地多,也容易迷糊当中。上一节我们画的波形图,主要是为了让读者更清晰地理解功能、计数器和信号的关系。如果我们牢记明德扬的规则,我们的设计将非常简单。我们要做的不是波形设计,而是功能设计。功能设计就是根据功能需求,编写我们的设计代码。我们以上一节中的案例4为例,说明什么叫功能设计。案例4的功能要求是:当收到en=1时,dout间隔1个时钟后,产生2个时钟周期的高电平脉冲,并且重复3次。 由题意可知,要对“间隔”和“高电平”个数进行计数,但没有信号表示“高隔”,为此想出补充一个信号flag_add,用来表示计数区域。间隔时间+高电平时间,得到计数器数3个。                                我们看到重复3次这一句话,这就说明还有一个计数器计数重复的次数。自然地想到,每完成一次就加1,一共加3次。得到代码如下。 在设计计数器0的时候,新增了信号flag_add。那进一步思考,什么时候要产生动作,那就让flag_add为1。自然,从题意可知,en==1是开始,重复次数完了,那就结束,不用再产生信号。所以flag_add代码。 最后我们再来设计dout,由题意可知,每次均是间隔1个之后dout变1,2个时钟之后变0。那用什么来数这个1和2呢?cnt0。综合起来,就是说cnt0数到1个后,dout变1,数完后变0。 总结:从功能的文字描述中出发,根据功能要求来设计代码。在设计时,一定要理解清楚信号的因果关系,例如为什么变0,为什么变1,从功能说明中找答案。经常训练这种思考和设计方式,几分钟就能设计出精妙的代码,而且因果关系、逻辑关系清楚,几乎不存在出错的可能,从而写出所想即所得的代码。

  • 2018-09-19
  • 发表了主题帖: FPGA至简设计法经典案例3【1241003385】

    至简设计法经典案例3 案例3. 当收到en1=1时,dout产生3个时钟周期的高电平脉冲;当收到en2==1时,dout产生2个周期的高电平脉冲。                                       上面波形图显示了描述的功能。第3个时钟上升沿收到en1==1,所以dout变1并且持续3个时钟周期;在第9个时钟上升沿看到en2==1,所以dout变1并且持续2个时钟周期。注意,en1==1和en2==1的出现是没有顺序的。有读者可能会问,如果en1==1和en2==1同时出现,或者说在dout==1期间,出现了en1==1或者en2==1,该怎么办?请不要考虑这种情况,本案例假设永远不会出现该情况。明德扬在模块划分规范时,会要求各个模块之间配合清楚。否则每个模块都要处理所有情况,那就相当复杂了。看到大于1的数字,就知道要计数。推荐的计数方式如下: 计数器cnt都是计算dout==1的个数。不要考虑使用2个计数器来分别计数en1和en2的情况,这是因为即使用了2个计数器,这2个计数器都不是同时在计数的,不同时计数就说明可以合并。在确认计数器数多少个时,我们遇到了问题。因为这个计数器有时候数到3个就清零(en1==1触发的波形),有时候数到2个就清零(en2==1触发的波形)。此时,我们建议你用变量x代替,即数         到x个。注意,verilog是没有变量的概念的,这个变量,是明德扬提出的一个设计概念,x本质上还是一个信号。引入变量有什么用呢?设计计数器时就方便了,该计数器加1条件是dout==1,数x个就结束,因此代码如下: 甚至我们还可以写出dout的代码,dout变1的条件是:en1==1或者en2==1;变0的条件是:计数器数完了。所以代码如下: 我们再设计一下变量x,我们知道计数器en1==1触发的时候数3个就清零,en2==1触发的时候数到2个就清零,为此增加一个信号flag_sel来区分这两种情况,flag_sel==0表示是en1==1触发的,flag_sel==1表示是en2==1触发的,波形如下: flag_sel变0的条件是遇到en1==1,flag_sel变1的条件是遇到en2==1,为此flag_sel的代码如下。 有了flag_sel,我们就好区分x的值了。flag_sel为0时,x为3(数3个清零);flag_sel为1时,x为2(数2个清零),此时要用组合逻辑设计x,不然会出错的。代码如下: 至此,本工程的主体程序已经设计完毕,本题,我们使用了变量x,这是明德扬的至简设计方法中的变量法。 将module的名称定义为my_ex3。并且我们已经知道该模块有5个信号:clk、rst_n、en1、en2和dout。为此,代码如下: 其中clk、rst_n、en1和en2是输入信号,dout是输出信号,并且5个信号都是1比特的,根据这些信息,我们补充输入输出端口定义。代码如下: 接下来定义信号类型。cnt是用always产生的信号,因此类型为reg。cnt计数的最大值为2,需要用2根线表示,即位宽是2位。add_cnt和end_cnt都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下: dout是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下: flag_sel是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下: x是用always方式设计的,因此类型为reg,并且其值最大为3,用2根线表示即可。因此代码如下: 至此,整个代码的设计工作已经完成。整体代码如下:1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28module my_ex3(        clk      ,        rst_n    ,        en1      ,        en2      ,        dout          );     input    clk      ;  input     rst_n   ;  input     en1     ;  input     en2     ;  output    dout    ;           reg   [ 1:0]   cnt      ;  wire          add_cnt  ;  wire          end_cnt  ;  reg           dout     ;  reg           flag_sel ;  reg [ 1:0]    x         ;     always @(posedge clk or negedge  rst_n)begin       if(!rst_n)begin           cnt <= 0;       end       else if(add_cnt)begin           if(end_cnt)              cnt <= 0;           else              cnt <= cnt + 1;       end  end     assign add_cnt = dout==1;         assign end_cnt = add_cnt &&  cnt==x-1 ;           always   @(posedge clk or negedge rst_n)begin       if(rst_n==1'b0)begin           dout <= 0;       end       else if(en1==1 || en2==1)begin           dout <= 1;       end       else if(end_cnt)begin           dout <= 0;       end  end        always   @(posedge clk or negedge rst_n)begin       if(rst_n==1'b0)begin           flag_sel <= 0;       end       else if(en2==1)begin           flag_sel <= 1;       end       else if(en1==1)begin           flag_sel <= 0;       end  end     always   @(*)begin       if(flag_sel==0)           x = 3;       else           x = 2;  end     endmodule 总结:设计时,我们不要受具体数字的影响,而是仔细识别信号的一致性动作,然后利用变量法来设计。这样就能设计出精妙的代码。

  • 2018-09-18
  • 发表了主题帖: FPGA至简设计法案例4 【12401003385】

    至简设计法经典案例4 案例4. 当收到en=1时,dout间隔1个时钟后,产生2个时钟周期的高电平脉冲,并且重复3次。                                       上面波形图显示了描述的功能。第3个时钟上升沿收到en==1,所以dout间隔1个时钟后变1并且持续2个时钟周期,这个动作重复3次,结束。 看到大于1的数字,就知道要计数。下面的计数方式非常普遍: 即用一个计数器,从头数到尾。这个计数器的设计很简单,但产生dout信号就不容易了。 明德扬推荐的计数方式如下: 利用2个计数器。cnt0就如案例2一样,数的是间隔和高电平时钟;而计数器cnt1数的是重复次数。如案例2相同,需要添加信号flag_add来指示cnt0的加1区域,波形如下图。 所以cnt0的加1条件是flag_add==1,计数3个就清零。仔细观察cnt1可以看到,每次cnt0数完后,cnt1就会加1。所以cnt1的加1条件是end_cnt0,计数3个就清零。从而我们可以设计出cnt0和cnt1的代码,输入Jsq2,即可调出模板。 flag_add有两个变化点:变1和变0。变1是因为en==1,变0是因为重复次数都完了,也就是end_cnt1。所以flag_add代码如下。 dout有两个变化点:变1和变0。在cnt0数到1时(一个间隔)时变1,在cnt0数完时变0,所以dout的代码如下。 至此,本工程的主体程序已经设计完毕,之后需要读者补充信号定义、输入输出定义了。 将module的名称定义为my_ex3。并且我们已经知道该模块有5个信号:clk、rst_n、en和dout。为此,代码如下: 其中clk、rst_n、en是输入信号,dout是输出信号,并且4个信号都是1比特的,根据这些信息,我们补充输入输出端口定义。代码如下: 接下来定义信号类型。cnt0是用always产生的信号,因此类型为reg。cnt0计数的最大值为2,需要用2根线表示,即位宽是2位。add_cnt0和end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下: cnt1是用always产生的信号,因此类型为reg。cnt1计数的最大值为2,需要用2根线表示,即位宽是2位。add_cnt1和end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下: dout是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下: flag_add是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下: 至此,整个代码的设计工作已经完成。整体代码如下:1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28module my_ex4(        clk      ,        rst_n    ,        en       ,        dout          );     input     clk     ;  input     rst_n   ;  input     en      ;  output    dout    ;        reg    [ 1:0]    cnt0     ;  wire            add_cnt0 ;  wire            end_cnt0 ;     reg    [ 1:0]    cnt1     ;  wire            add_cnt1 ;  wire            end_cnt1 ;  always @(posedge clk or negedge  rst_n)begin       if(!rst_n)begin           cnt0 <= 0;       end       else if(add_cnt0)begin           if(end_cnt0)              cnt0 <= 0;           else              cnt0 <= cnt0 + 1;       end  end     assign add_cnt0 = flag_add==1;  assign end_cnt0 = add_cnt0 &&  cnt0==3-1 ;     always @(posedge clk or negedge  rst_n)begin        if(!rst_n)begin           cnt1 <= 0;       end       else if(add_cnt1)begin           if(end_cnt1)              cnt1 <= 0;           else              cnt1 <= cnt1 + 1;       end  end     assign add_cnt1 = end_cnt0;  assign end_cnt1 = add_cnt1 &&  cnt1==3-1 ;        reg           flag_add  ;     always   @(posedge clk or negedge rst_n)begin       if(rst_n==1'b0)begin           flag_add <= 0;       end       else if(en==1)begin           flag_add <= 1;       end       else if(end_cnt1)begin           flag_add <= 0;       end  end     reg         dout    ;     always   @(posedge clk or negedge rst_n)begin       if(rst_n==1'b0)begin           dout <= 0;       end       else if(add_cnt0 && cnt0==1-1)begin           dout <= 1;       end       else if(end_cnt0)begin           dout <= 0;       end  end     endmodule 本题中,我们设计了2个计数器,从而使得dout的设计非常简单。计数器的组合使用,对设计的复杂度有非常大的影响。合理和正确使用,将能设计出赏心悦目的代码。 此内容由EEWORLD论坛网友guyu_1原创,如需转载或用于商业用途需征得作者同意并注明出处

最近访客

< 1/2 >

统计信息

已有28人来访过

  • 芯币:106
  • 好友:--
  • 主题:28
  • 回复:3
  • 课时:--
  • 资源:--

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言