工程师深度:FPGA 高手养成记

 admin   2020-10-15 07:40   12 人阅读  0 条评论
原标题:工程师深度:FPGA 高手养成记

Verliog语法基础

基本的语法略过,主要想写一些关于框架,规范,技术难点的博文,这样对于我们养成好的编码习惯是有好处的,就定这样一个flag吧.希望大家可以一起好好学习,共同进步.

接口时序设计规范

模块和模块之间的通过模块的接口实现关联, 因此规范的时序设计, 对于程序设计的过程, 以及程序的维护, 团队之间的沟通都是非常必要的。

工程师深度:FPGA 高手养成记

命名规则

1、 顶层文件

对象+功能+top

比如:video_oneline_top

2、 逻辑控制文件

介于顶层和驱动层文件之间

对象+ctr

比如:ddr_ctr.v

3、 驱动程序命名

对象+功能+dri

比如:lcd_dri.v、 uart_rxd_dri.v

4、 参数文件命名

展开全文

对象+para

比如:lcd_para.v

5、 模块接口命名:文件名+u

比如 lcd_dir lcd_dir_u(........)

6、 模块接口命名:特征名+文件名+u

比如 mcb_read c3_mcb_read_u

7、 程序注释说明

// Tool versions: ISE14.7// Deion: CY7C68013A SLAVE FIFO comunication with fpga// Revision: V1.0// Additional Comments://1) _i input//2) _o output//3) _n activ low//4) _dg debug signal//5) _r delay or register//6) _s state mechine/*****************************************************************/

8、 端口注释

input Video_vs_i,//输入场同步入

9、 信号命名

命名总体规则:对象+功能(+极性) +特性

10、 时钟信号

对象+功能+特性

比如:phy_txclk_i、 sys_50mhz_i

11、 复位信号

对象+功能+极性+特性

比如:phy_rst_n_i、 sys_rst_n_i

12、 延迟信号

对象+功能+特性 1+特征 2

比如:fram_sync_i_r0、 fram_sync_i_r1

13、 特定功能计数器

对象+cnt

比如:line_cnt、 div_cnt0、 div_cnt1

功能+cnt

比如:wr_cnt、 rd_cnt

对象+功能+cnt

比如:fifo_wr_cnt、 mcb_wr_cnt、 mem_wr_cnt

对象+对象+cnt

比如:video_line_cnt、 video_fram_cnt

14、 一般计数器

cnt+序号

用于不容易混淆的计数

比如:cnt0、 cnt1、 cnt2

15、 时序同步信号

对象+功能+特性

比如:line_sync_i、 fram_sysc_i

16、 使能信号

功能+en

比如:wr_en、 rd_en

对象+功能+en

比如:fifo_wr_en、 mcb_wr_en

Verilog 最最基础语法

C 语言和 Verilog 的关键词和结构对比:

工程师深度:FPGA 高手养成记

C 语言和 Verilog 运算符对比:

工程师深度:FPGA 高手养成记

关键字

信号部分:

input 关键词, 模块的输入信号, 比如 input Clk, Clk 是外面关键输入的时钟信号;

output 关键词, 模块的输出信号, 比如 output[3:0]Led; 这个地方正好是一组输出信号。其中[3:0]表示 0~3 共 4 路信号。

inout 模块输入输出双向信号。这种类型, 我们的例子 24LC02 中有使用。数总线的通信中, 这种信号被广泛应用;

wire 关键词, 线信号。例如:wire C1_Clk; 其中 C1_Clk 就是 wire 类型的信号;

线信号,三态类型, 我们一般常用的线信号类型有input,output,inout,wire;

reg 关键词, 寄存器。和线信号不同, 它可以在 always 中被赋值, 经常用于时序逻辑中。比如 reg[3:0]Led;表示了一组寄存器。

结构部分:

代表一个模块, 我们的代码写在这个两个关键字中间

always@括号里面是敏感信号。这里的 always@(posedge Clk)敏感信号是 posedge Clk 含义是在上升沿的时候有效, 敏感信号还可以 negedge Clk 含义是下降沿的时候有效, 这种形式一般时序逻辑都会用到。还可以是*这个一符号, 如果是一个*则表示一直是敏感的, 一般用于组合逻辑。

assign 用来给 output,inout 以及 wire 这些类型进行连线。assign 相当于一条连线, 将表达式右边的电路直接通过 wire(线)连接到左边, 左边信号必须是 wire 型(output 和 inout 属于 wire 型) 。当右边变化了左边立马变化, 方便用来描述简单的组合逻辑。

符号部分:

这里重点讲解一下“ <=” 赋值符号,非阻塞赋值 ,“ =” 阻塞赋值,“ {}

“<=” 赋值符号, 非阻塞赋值, 在一个 always 模块中, 所有语句一起更新。它也可以表示小于等于, 具体是什么含义编译环境根据当前编程环境判断, 如果“<=” 是用在一个 if 判断里如:if(a <= 10);当然就表示小于等于了。

“=” 阻塞赋值, 或者给信号赋值, 如果在 always 模块中, 这条语句被立刻执行。阻塞赋值和非阻塞赋值将再后面详细举例说明。

参数部分:

parameter

预处理命令

Verilog 中数值表示的方式

如果我们要表示一个十进制是 180 的数值, 在 Verilog 中的表示方法如下:

二进制:8’ b1010_1010; //其中“_” 是为了容易观察位数, 可有可无。

十进制:8’ d180;

16 进制:8’ hAA;

讲到这里, 具备这些基础知识, 需要通过代码来学习Veriog 语言。最后, 笔者提一点建议, 学习 Verilog 多看别人写的优秀的代码, 多看官方提供的代码和文档。其中官方提供的代码, 很多时候代表了最新的用法, 或者推荐的用法。读者学习, 首先把最最基础的掌握好, 这样, 在项目中遇到了问题, 也能快速学习, 快速解决。

对于理论知识的学习, 没必要一开始就研究得那么深刻, 只是搞理论学习, 对于学习Verilog 语言, 或者 FPGA 开发是不实际的, 要联系理论和实践结合。多仿真, 多验证, 多问题, 多学习, 多改进.

浅谈状态机

01. 前言

状态机是FPGA设计中一种非常重要、非常根基的设计思想,堪称FPGA的灵魂,贯穿FPGA设计的始终。

02. 状态机简介

什么是状态机:状态机通过不同的状态迁移来完成特定的逻辑操作(时序操作)状态机是许多数字系统的核心部件, 是一类重要的时序逻辑电路。通常包括三个部分:

  • 下一个状态的逻辑电路
  • 存储状态机当前状态的时序逻辑电路
  • 输出组合逻辑电路

03. 状态机分类

通常, 状态机的状态数量有限, 称为有限状态机(FSM) 。由于状态机所有触发器的时钟由同一脉冲边沿触发, 故也称之为同步状态机。

根据状态机的输出信号是否与电路的输入有关分为 Mealy 型状态机和 Moore 型状态机

3.1,Mealy 型状态机

电路的输出信号不仅与电路当前状态有关, 还与电路的输入有关

工程师深度:FPGA 高手养成记

3.2, Moore 型状态机

电路的输出仅仅与各触发器的状态, 不受电路输入信号影响或无输入

工程师深度:FPGA 高手养成记

状态机的状态转移图, 通常也可根据输入和内部条件画出。一般来说, 状态机的设计 包含下列设计步骤:

  • 根据需求和设计原则, 确定是 Moore 型还是 Mealy 型状态机;
  • 分析状态机的所有状态, 对每一状态选择合适的编码方式, 进行编码;
  • 根据状态转移关系和输出绘出状态转移图;
  • 构建合适的状态机结构, 对状态机进行硬件描述。

04. 状态机描述

状态机的描述通常有三种方法, 称为一段式状态机, 二段式状态机和三段式状态机。

状态机的描述通常包含以下四部分:

  • 利用参数定义语句 parameter 描述状态机各个状态名称, 即状态编码。状态编码通常有很多方法包含自然二进制编码, One-hot 编码,格雷编码码等;
  • 用时序的 always 块描述状态触发器实现状态存储;
  • 使用敏感表和 case 语句(也采用 if-else 等价语句) 描述状态转换逻辑;
  • 描述状态机的输出逻辑。

下面根据状态机的三种方法来具体说明

4.1,一段式状态机

一段式状态机是应该避免使用的, 该写法仅仅适用于非常简单的状态机设计。

4.2, 两段式状态机

两段式状态机采用两个 always 模块实现状态机的功能, 其中一个 always 采用同步时序逻辑描述状态转移, 另一个 always 采用组合逻辑来判断状态条件转移。

4.3, 三段式状态机

assign out_o=out_r;endmodule

三段式状态机在第一个 always 模块采用同步时序逻辑方式描述状态转移, 第二个always 模块采用组合逻辑方式描述状态转移规律, 第三个 always 描述电路的输出。通常让输出信号经过寄存器缓存之后再输出, 消除电路毛刺。

05. 状态机优缺点

1、一段式状态机:只涉及时序电路,没有竞争与冒险,同时消耗逻辑比较少。

但是如果状态非常多,一段式状态机显得比较臃肿,不利于维护。

2、两段式状态机:当一个模块采用时序(状态转移),一个模块采用组合时候(状态机输出),组合逻辑电路容易造成竞争与冒险;当两个模块都采用时序,可以避免竞争与冒险的存在,但是整个状态机的时序上会延时一个周期。

两段式状态机是推荐的状态机设计方法。

3、三段式状态机:三段式状态机在状态转移时采用组合逻辑电路+格雷码,避免了组合逻辑的竞争与冒险;状态机输出采用了同步寄存器输出,也可以避免组合逻辑电路的竞争与冒险;采用这两种方法极大的降低了竞争冒险。并且在状态机的采用这种组合逻辑电路+次态寄存器输出,避免了两段式状态机的延时一个周期(三段式状态机在上一状态中根据输入条件判断当前状态的输出,从而在不插入额外时钟节拍的前提下,实现寄存器的输出)。

三段式状态机也是比较推崇的,主要是由于维护方便, 组合逻辑与时序逻辑完全独立。

06. 总结

灵活选择状态机,不一定要拘泥理论,怎样方便怎样来

07.扩展

四段式不是指三个always代码,而是四段程序。使用四段式的写法,可参照明德扬GVIM特色指令Ztj产生的状态机模板。

明·德·扬四段式状态机符合一次只考虑一个因素的设计理念。

  • 第一段代码,照抄格式,完全不用想其他的。
  • 第二段代码,只考虑状态之间的跳转,也就是说各个状态机之间跳转关系。
  • 第三段代码,只考虑跳转条件。
  • 第四段,每个信号逐个设计。

有兴趣的话可以自己去学习一下,或者 http://www.mdyedu.com/product/299.html 自行看视频学习一下,努力,奋斗......

Test bench文件结构一览无余

01,前言

Verilog测试平台是一个例化的待测(MUT)模块,重要的是给它施加激励并观测其输出。逻辑模块与其对应的测试平台共同组成仿真模型,应用这个模型可以测试该模块能否符合自己的设计要求。

编写TESTBENCH的目的是为了对使用硬件描述语言设计的电路进行仿真验证,测试设计电路的功能、性能与设计的预期是否相符。通常,编写测试文件的过程如下:

  • 产生模拟激励(波形);
  • 将产生的激励加入到被测试模块中并观察其响应;
  • 将输出响应与期望值相比较。

02,完成的Test bench文件结构

通常,一个完整的测试文件其结构为

  • module Test_bench;//通常无输入无输出
  • 信号或变量声明定义
  • 逻辑设计中输入对应reg型
  • 逻辑设计中输出对应wire型
  • 使用initial或always语句产生激励
  • 例化待测试模块
  • 监控和比较输出响应
  • endmodule

03,时钟激励设计

下面列举出一些常用的封装子程序, 这些是常用的写法, 在很多应用中都能用到。

3.1,时钟激励产生方法一

50%占空比时钟

3.2, 时钟激励产生方法二

50%占空比时钟

3.3,时钟激励产生方法三:

产生固定数量的时钟脉冲

3.4,时钟激励产生方法四

产生非占空比为50%的时钟

04,复位信号设计

4.1,复位信号产生方法一

异步复位

4.2,复位信号产生方法二

同步复位

4.3复位信号产生方法三

复位任务封装

05,双向信号设计

5.1,双向信号描述一

inout在testbench中定义为wire型变量

5.2双向信号描述二

强制force

06,特殊信号设计

6.1特殊激励信号产生描述一

输入信号任务封装

6.2特殊激励信号产生描述二

多输入信号任务封装

6.3,特殊激励信号产生描述三

输入信号产生,一次SRAM写信号产生

Testbench中@与wait

07,仿真控制语句及系统任务描述

7.1,仿真控制语句及系统任务描述

7.2,仿真终端显示描述

7.3文本输入方式

/*mem.dat文件内容

//仿真输出为

08,总结

一个完整的设计,除了好的功能描述代码,对于程序的仿真验证是必不可少的。学会如何去验证自己所写的程序,即如何调试自己的程序是一件非常重要的事情。而RTL逻辑设计中,学会根据硬件逻辑来写测试程序,即Testbench是尤其重要的。

【很重要】Testbenth前仿真全过程

01. 前言

在FPGA 高手养成记-Test bench文件结构一览无余 只是简单的例举了常用的 testbench 写法,在工程应用中基本能够满足我们需求, 至于其他更为复杂的 testbench 写法, 大家可参考其他书籍或资料。

testbench没有像RTL代码设计那样严谨,我们可以在符合语法规则的前提下,随意编写我们的测试文件,有些在RTL代码中不可综合的语句,我们可以在testbench中实现。大体流程如下:

工程师深度:FPGA 高手养成记

02. 测试模块设计

要测试我们的cpu需要ROM和RAM模块,这就需要我们先做好这两个模块

工程师深度:FPGA 高手养成记

这里定义了一个 1024 x 8 的RAM

工程师深度:FPGA 高手养成记

再定义一个8192 x 8 的ROM

ROM和RAM都还没有装入数据,等会我们会调用函数给他们装数据,接下来是地址译码器,来控制ROM和RAM的打开与关闭。

工程师深度:FPGA 高手养成记

各模块建立好之后我们就开始仿真了。

03.仿真

这次教学我们用的是modelsim SE 10.0 版本进行教学,直接先在quartus II中建一个.v文件将其保存在原来的工程文件目录中,并命名为cpu_top.v,直接在这里写测试代码

工程师深度:FPGA 高手养成记

下面大家可以来完成cpu 的仿真过程了

3.1,模块包含

首先,我们需要将我们刚写好的那几个模块包含进去,即CPU模块,ROM模块,RAM模块,地址译码器模块,并写好时间测量度,见下图

工程师深度:FPGA 高手养成记

3.2,定义顶层模块

由于我们的设计只有两个输入,即时钟模块和复位模块,凡是输入信号在testbench中统一定义成reg型变量,凡是输出或者双向输入输出信号统一定义成wire型变量,我们的设计只有输入没有输出,故只定义输入和连线即可

工程师深度:FPGA 高手养成记

下图便是我们要组成的测试顶层模块图,我们定义的wire型变量,实际就是我们顶层模块中,模块模块与模块间的连线。而这些连线就是我们cpu的输出,这样我们就可以用我们的测试模块来测试我们的cpu是否能正确工作

工程师深度:FPGA 高手养成记

3.3. 元件例化

就是将各个模块连接起来即可,这里就不做太多的说明了,因为以前都写过很多次了

工程师深度:FPGA 高手养成记

3.4.测试激励的书写

先写好时钟产生模块和复位模块.并将复位模块用task任务封装,这样我们在测试过程中就可以随时调用复位任务进行复位

时钟为50Mhz,复位时间为20ns

工程师深度:FPGA 高手养成记

然后,我们再用task封装我们需要的模块,我们来想一下,上电后,CPU会从ROM中读两个时钟周期的数据是吧,但是我们的ROM现在还是空的,所以我们需要一个任务是往ROM中装入程序,给ROM中装数据我们可以用系统函数$readmemb,即打开一个文件,并将其中的数据送到我们之前定义的ROM中去

工程师深度:FPGA 高手养成记

而test1.pro文件是需要我们自己定义的,我们可以在quartusII中再新建一个.v文件,在里面写上我们自己定义的程序,并将其保存为.pro文件即可,至于写什么程序,是我们随便定义的.

工程师深度:FPGA 高手养成记

装完ROM和RAM的数据之后,按说就可以了进行波形仿真了,因为cpu是自动读取数据的,下面我们先来做第一步仿真,我先把之后的代码注释掉,大家先看没有被注释掉的代码

工程师深度:FPGA 高手养成记

里面都是我们之前封装好的函数,刚开始进行复位,然后进行第一步测试,之后停止,将其保存之后,并默认为用其打开,打开后见下图

工程师深度:FPGA 高手养成记

然后,file——new——library——ok即建好一个库

工程师深度:FPGA 高手养成记

点击左上角的编译按钮,将我们之前写好的所有.v文件全部都编译进去

工程师深度:FPGA 高手养成记

看到tran一栏显示编译成功后即可,若没有tran一栏,可以选择菜单中的view——tran即可,若显示有红色错误,那就请读者按照它的要求进行修改代码,这说明你的代码有问题,一般是连接问题

工程师深度:FPGA 高手养成记

3.5,波形仿真

编译成功后,双击cpu_top就可以开始波形仿真了

工程师深度:FPGA 高手养成记

进入仿真页面后,我们右击cpu模块将其加入至波形

工程师深度:FPGA 高手养成记

大家先看两个图,等会结合这两个图给大家细细讲解仿真过程

工程师深度:FPGA 高手养成记

工程师深度:FPGA 高手养成记

工程师深度:FPGA 高手养成记

我们先来看第一个过程

工程师深度:FPGA 高手养成记

上电后,cpu先从ROM中读回两个周期的数据,是从ROM的0地址开始的,再对比我们之前定义好的ROM,数据读取正确,读回的数据的前三位是111,即指令码JMP,后13位003c为地址码,JMP指令是将读回的数据作为新的地址码来读取相应地址的数据。那么,下一步,cpu应该是从ROM的003c地址处读数据才对,再看一下波形

工程师深度:FPGA 高手养成记

对比波形后可知,cpu正好是从003c处读取数据,读到的数据指令码位111即JMP,地址码位0006,再到ROM的0006地址处看

工程师深度:FPGA 高手养成记

这次读回的指令码位101,即LDA,也就是说将后13位地址码对应的RAM中的数据读回,送到累加器中,想一下,这时的RAM应该是打开的,而且双向输入输出口的数据总线上应该是来自RAM的8位数据,由于ROM0006地址处的地址码为1800是13位的,而RAM的地址是9位的,因此实际上我们从RAM中读回的数据是从RAM的0地址读回的,即我们之前给RAM写好的0000_0000,再看一下波形

工程师深度:FPGA 高手养成记

正如我们所想的一样,数据总线上是0000_0000,RAM是打开的,地址为1800

就这样,读者可以自己再试一下,看看我们的cpu是不是按照我们之前给他的程序运行的,在这里我就不再给大家一一介绍了

虽然波形仿真很直观,但是看久了就会令人眼花缭乱,尤其是数据很多的时候,我们只能看其中一部分,不能讲所有数据看完整,这时候我们单单是用波形来仿真就远远不够了,下面介绍用系统任务仿真的过程

3.6,系统任务仿真

再回到我们的代码,注释掉了一些代码吧,我们把那些代码给加上,以其中一个过程为例

工程师深度:FPGA 高手养成记

假设读回的指令码位101,即LDA,如果我在fentch_8的高电平期间且在cpu输出地址为奇数的时候记录一下此时的时间、指令、地址、目的地址、数据的话就可以不用看波形,让电脑来帮助我们来分析了,因此作如下处理

工程师深度:FPGA 高手养成记

这里我延时60ns,是因为第一次记录的时候数据总线上还没有数据,只有延时一会才会有数据,即上面那张波形图右边那根黄色的线处记录一下数据,并将其显示。我们也可以加上一下标注,来帮助我们观察

这样我们再来仿真的时候就不用看波形了,直接打开tran一栏观察记录即可

工程师深度:FPGA 高手养成记

这样便可以为我们省下大量的仿真时间

04. 总结

这里提出以下几点建议供大家参考:

• 封装有用且常用的 testbench, testbench 中可以使用 task 或 function 对代码进行封装, 下次利用时灵活调用即可;

• 如果待测试文件中存在双向信号(inout)需要注意, 需要一个 reg 变量来表示输入, 一个 wire 变量表示输出;

• 单个 initial 语句不要太复杂,可分开写成多个 initial 语句, 便于阅读和修改;

• Testbench 说到底是依赖 PC 软件平台, 必须与自身设计的硬件功能相搭配。

串行口通信电路设计

1、顶层模块

写程序都一样,不能多有的程序都写在一个模块里,那样看起来很麻烦,出了错误也不好维护,对于一些小的程序我们可以写在一个模块里,但程序一旦复杂起来还是要懂得模块化编程的,对于顶层模块,最好是只写接口就好了,例如:

工程师深度:FPGA 高手养成记

这段代码中,rx_232是我们的底层模块名,后面跟着的那个rx呢是我们自己取的名字,是任意的。后面的一大串呢就是接口,为了直观呢,建议大家采用我的这种写法,看上去比较清楚明白,括号里面的接口是我们顶层文件的接口,括号外面的是我们调用底层模块的接口,这些接口要一一对应正确才能保证数据之间的传输。

在顶层模块中,我们只定义了数据输入接口,用来接收数据,数据输出接口,用于发送数据,时钟接口,和复位接口。这四个接口是有输入输出关系的,对于其他的接口,是属于我们整个模块内部的接口,是模块与模块之间的接口,既非输入,也非输出,相当于一根导线一样,所以我们把他们定义成wire型变量

工程师深度:FPGA 高手养成记

2、波特率选择模块

单片机或者计算机在串口通信时的传输速率用波特率表示,9600bps表示的就是每秒钟传送9600位的数据,这里之所以计数到5027,在这里算一下。

工程师深度:FPGA 高手养成记

我再来解释一下这个图吧,我当时学单片机的时候还真是没怎么重视这张图,只知道只要一个指令就可以发送,没有真正搞清楚是怎么发送和接受的,那就在这里复习一下吧,计算机和单片机之间进行通信,这里用的是rs232通信方式,即通信之前,计算机和单片机之前要设定好相同的波特率,只有波特率相同了才能进行通信。

其次,计算机发送数据时要先发送一个起始位,一般是低电平,后面跟着的是8位数据位,奇偶校验位,停止位等,当起始位低电平信号传送到我们的接收端口时,在接收模块中会发送一个命令给波特率时钟计数器,开始计时,计时到一半的时候会产生一个采样高脉冲信号,当接收模块检测到这个高脉冲之后就会将数据存到寄存器中,当检测到第11个脉冲信号时,也就是代表一帧的数据接收完毕,发送模块就给波特率选择模块发送一个停止信号告诉它停止计时。

同时,当数据接收完毕之后也会产生一个信号告诉发送模块,信号已经接收完毕,准备发送,这个时候发送模块再给波特率计时模块发送一个信号开始计时,计数到某一位的中间时产生一个采样信号,当发送模块检测到采样信号之后就将寄存器里的数据送到发送端,每次只送一位,这样就实现了数据的接收与发送。

下面是波特率计时模块的主要程序部分

工程师深度:FPGA 高手养成记

3、数据接收模块

在接收模块中,为了准确的检测计算机发送来的数据起始位的那个低电平信号,用到了边沿脉冲检测法,可以有效的避免毛刺现象带来的问题

工程师深度:FPGA 高手养成记

下面是发送部分的主要程序段

工程师深度:FPGA 高手养成记

4、数据发送模块

发送模块原理上和接受模块是一样的,不同点就是接收模块通过边沿检测法检测起始位低电平信号来启动接收数据,而发送模块是通过检测数据发送完毕后,我们认为得置一个低电平信号,发送模块通过检测这个低电平信号来启动发送。见下图

工程师深度:FPGA 高手养成记

下面是生成的RTL视图

工程师深度:FPGA 高手养成记

下面是测试结果

工程师深度:FPGA 高手养成记

手把手解析时序逻辑乘法器代码

下面是一段16位乘法器的代码,大家可以先浏览一下,之后我再做详细解释

input clk; //芯片的时钟信号。input rst_n; //低电平复位、清零信号。定义为0表示芯片复位;定义为1表示复位信号无效。input start; //芯片使能信号。定义为0表示信号无效;定义为1表示芯片读入输入管脚得乘数和被乘数,并将乘积复位清零。input[15:0] ain; //输入a(被乘数),其数据位宽为16bit.input[15:0] bin; //输入b(乘数),其数据位宽为16bit.output[31:0] yout; //乘积输出,其数据位宽为32bit.output done; //芯片输出标志信号。定义为1表示乘法运算完成.

reg[15:0] areg; //乘数a寄存器reg[15:0] breg; //乘数b寄存器reg[31:0] yout_r; //乘积寄存器reg done_r;reg[4:0] i; //移位次数寄存器//------------------------------------------------//数据位控制always @(posedge clk or negedge rst_n)if(!rst_n) i <= 5'd0;else if(start && i < 5'd17) i <= i+1'b1; else if(!start) i <= 5'd0;

//------------------------------------------------//乘法运算完成标志信号产生always @(posedge clk or negedge rst_n)if(!rst_n) done_r <= 1'b0;else if(i == 5'd16) done_r <= 1'b1; //乘法运算完成标志else if(i == 5'd17) done_r <= 1'b0; //标志位撤销

assign done = done_r;

//------------------------------------------------//专用寄存器进行移位累加运算always @(posedge clk or negedge rst_n) beginif(!rst_n) begin areg <= 16'h0000;breg <= 16'h0000;yout_r <= 32'h00000000;endelse if(start) begin //启动运算if(i == 5'd0) begin //锁存乘数、被乘数areg <= ain;breg <= bin;endelse if(i > 5'd0 && i < 5'd16) beginif(areg[i-1]) yout_r = {1'b0,yout[30:15]+breg,yout_r[14:1]}; //累加并移位else yout_r <= yout_r>>1; //移位不累加endelse if(i == 5'd16 && areg[15]) yout_r[31:16] <= yout_r[31:16]+breg; //累加不移位endend

assign yout = yout_r;endmodule

要理解这段代码,首先要弄明白几个点。

1、我们通常写的十进制的乘法竖式,同样适用于二进制。下面我们就以这个算式为例:1011 x 0111 =0100_1101。

2、两个16位的数相乘,结果是32位的,没有32位要在高位补零。

3、计算两个16位的数相乘需要移位15次。

例如:

1 0 1 1

x 0 1 1 1

------------------------------------

1 0 1 1

1 0 1 1

1 0 1 1

0 0 0 0

------------------------------------

1 0 0 1 1 0 1

前三次计算是移位的,最后一次没有移位

4、两个16位的数相加,结果是17位的,不够17位最高位补零。

例如语句yout[30:15]+breg,结果是17位的。

知道了这些,我们就开始看代码了

1)、接口部分注释写的很清楚,这里就不提了

2 ) 、数据位控制部分

当start为1时,芯片读入两个数,此时开始计数,计数16次,乘法运算开始

3 ) 、乘法运算完成标志信号产生

assign done = done_r;

这部分也很好理解

4 ) 、专用寄存器进行移位累加运算

这里为了简单,就用15到18位代替15到30位

工程师深度:FPGA 高手养成记

工程师深度:FPGA 高手养成记

工程师深度:FPGA 高手养成记

以上部分是最主要的计算部分,其他地方相对来说还比较简单,例如当乘数某一位为0时,不用累加,直接右移,当i计数到16时,此时就不用再移位了,可以直接用位数表示,直接累加即可。

下面是仿真图

工程师深度:FPGA 高手养成记

基于FIFO的串口发送机设计全流程

首先来解释一下FIFO的含义,FIFO就是First Input First Output的缩写,就是先入先出的意思,按照我的理解就是,先进去的数据先出,例如一个数组的高位先进,那么读出来的时候也就高位先出。下面是百度百科的解释。

FIFO一般用于不同时钟域之间的数据传输,比如FIFO的一端是AD数据采集,另一端是计算机的PCI总线,假设其AD采集的速率为16位 100K SPS,那么每秒的数据量为100K×16bit=1.6Mbps,而PCI总线的速度为33MHz,总线宽度32bit,其最大传输速率为1056Mbps,在两个不同的时钟域间就可以采用FIFO来作为数据缓冲。另外对于不同宽度的数据接口也可以用FIFO,例如单片机为8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。

工程师深度:FPGA 高手养成记

我们将这三个模块分别定义为dataoutput块,fifo_ctrl块和uart_ctrl块。现在考虑连线,具体到每一根连线,这样根据图来写代码要比直接用脑子构图要方便的多。三个模块,先考虑时钟和复位信号线,三个模块都有,然后,数据产生模块要将产生的数据发给FIFO模块,所以要有数据写入线,我们定义它为wr-datain,数据写入FIFO块后总要输出,这些数据就是我们要发送的数据,所以定义输出数据线tx_data,先不管FIFO,我们再来定义数据发送模块的连线,数据发送总要有个启动信号,所以我们定义变量tx_start,之后,还要有一个输出端给PC机,我们定义这个输出端位rs232。

对于FIFO模块的例化过程很简单就不做过多的说明,只把接口说一下,FIFO模块除了时钟,复位信号外,还有数据输入端口,这个端口要和之前的数据产生模块的数据输出端口相连,还有写请求端口,高电平有效,数据发送模块每隔1秒钟产生一个16位的数据,并发送写请求命令给FIFO,还有读请求命令,高电平有效数据发送模块在发送数据时要发送一个读请求给FIFO,从中读取数据后再发送给PC机,还有空信号empty,只要检测到FIFO中有数据,empty就为低电平,我们可用这个信号来启动数据发送模块。这样一来,我们的整体框架就出来了有了这个整体框架,再写代码就容易多了。

下面是RTL视图

工程师深度:FPGA 高手养成记

按照这个框架,先把接口定义出来,中间的连线用wire型

工程师深度:FPGA 高手养成记

设计完端口之后我们就来设计底层模块,先设计数据产生模块dataoutput,这个部分主要是产生数据,可用一个分频电路实现每1s发送一次的数据,产生这16位数据的时候,需要16个时钟,每个时钟数据自加1,总体来说比较简单

工程师深度:FPGA 高手养成记

写完一个模块之后养成好习惯,马上把端口例化

工程师深度:FPGA 高手养成记

数据产生以后就要进入缓冲器FIFO,由于这段代码我们是调用的,所以只要例化接口就好了,只需要将产生的fifo_ctrl_inst文件中例化好的代码拷贝粘贴就好

工程师深度:FPGA 高手养成记

最后我们要写数据发送部分,之前已经讲过,数据发送部分还要包括两个子模块,一个是波特率匹配模块,一个是发送模块,既然又包括两个子模块,那么我们还要构建一个框图

按照之前的例子,当FIFO当中有数据时empty就会拉低,我们把它取反后送给发送模块,告诉发送模块准备发送,这样,发送模块就会产生一个波特率计数器启动信号bps_start给波特率匹配模块,波特率匹配模块收到信号后立马开始匹配计数,并产生采集信号,将采集信号传给发送模块,发送模块根据采集信号,将数据一位一位发送出去。知道了这个原理之后,我们构建起这样一个框架

工程师深度:FPGA 高手养成记

根据这个框图,我们定义端口和线

工程师深度:FPGA 高手养成记

定义完端口之后,开始写发送模块,用边沿脉冲检测法检测启动信号tx_start信号的上升沿来启动发送部分,波特率配置模块具体代码在前面也文章中有给出,就不在说明,写完之后例化端口,这两个模块作为数据发送模块的子模块,要在数据发送模块下例化

工程师深度:FPGA 高手养成记

工程师深度:FPGA 高手养成记

这样一来,我们整个设计就完成了,看上去很简单,但是从我自己实践的角度来说还是有点挑战的,包括中间出现的各种问题,下面就来分享一下我在做这个设计时 遇到的问题

1.例化问题

在例化端口时,要注意括号里面的才是本层模块的端口,也就是说在本层模块上面已经定义过的变量,括号外面的才是被调用模块的端口,也在下层模块的顶部被声明,我在写这段程序的时候将二者颠倒了,导致连线不成功,最终是通过查看RTL视图知道了哪根线有问题才修改成功的

工程师深度:FPGA 高手养成记

2.同一个变量不能在多个always语句中被赋值

我们可能习惯这么写

那么,num的值在其他always语句中就不允许再被赋值或者清零,我在写的时候在其他always语句中将num 清零了,导致编译不成功

3. 定义变量之前不要出现该变量,即使后面又定义了

例如,我先进行num的运算,之后再定义num,reg [3:0] num,这样写的话虽然编译没有错误,但是在调用modelsim仿真的时候它会出现编译错误,所以为了规范,不要这样写

4. 在边沿脉冲检测的时候,习惯于检测下降沿,而这里是检测tx_en 的上升沿,所以我在复位清零的时候错误的将两级寄存器赋值为0,实际上在检测上升沿时要对两级寄存器复位时置一,再把最后一级寄存器取反后与上一级相与。

工程师深度:FPGA 高手养成记

5. 在发送数据部分,由于受到上次写接收部分程序的影响,没有将起始位发送出去,因为在接收部分,是不需要接收起始位的,是从第一位开始,而在发送部分只有先发送起始位才能和上位机握手通信,还有在发送完数据后要发送停止位,其他情况下都发送高电平来阻止通信的进行

工程师深度:FPGA 高手养成记

6.最后一个问题是最棘手的问题,我找了好大一半天也没发现,最后还是根据源代码找出来的,不过我还是不知道将这两条语句颠倒了对程序有什么影响,只知道颠倒后数据会一直在发送,不会像预设一样,每隔一秒发送一次,至今还是搞不清楚,希望大神指点迷津

工程师深度:FPGA 高手养成记

总结

语法上的错误到不至于太难,写的多了就不会出错了,关键是逻辑上的错误很隐蔽,也很难发现,可以通过RTL视图来检测连线上是否正确,还可以借助仿真工具。

从性能至关重要的 5G/通信和图像/视频处理设计,到启用了最低功耗边缘 AI/ML 的设计和 IP,Catapult HLS 如今正广泛应用于生产的方方面面。使用 HLS 的团队,能够处理不断变化的规范,同时将 FPGA 和 ASIC 中用于设计和验证的项目时间缩短一半以上。

本次研讨会除了基础入门,还将以NVIDIAGoogle 的实例,介绍如何实际使用Catapult HLS,讲解为什么 HLS 方法令设计实现和验证比传统 RTL 设计流程快 50%。

注册听课入口

学习内容

Catapult HLS 入门

1. Catapult HLS 的功能和状态,以及 AI/ML、图像处理、5G/通信和视频领域的产品级客户案例分析。

2. Catapult HLS 可通过 C++/SystemC 提供高质量的 RTL,并提高硬件设计和验证的抽象水平。此研讨会将提供以下方面基本概念的技术概述:HLS 的工作方式;HLS 如何在提供硬件设计抽象好处的同时,提供必要的 QoR 以取代手动编码的 RTL。

AI/ML 算法二维卷积及应用简介

将使用 YoloTiny AI/ML 算法进行对象检测和分类,以演示用于卷积神经网络的二维卷积为何适合用于加速以实现更低的功耗。

本次研讨会是为期四天的在线课程,除了以上关于HLS的基础概述,还将会有来自 Arm、Bosch、SK hynix、Facebook等开发案例。

课程亮点

• 4天4场研讨会,每场用时2小时

• 交互式分组研讨会,随时提问题,实时技术问答

推荐参加对象

• 有兴趣转向 HLS,大幅提高设计和验证效率的 RTL 设计人员;

• 图像处理、计算机视觉、机器和深度学习领域内,对创建节能型加速器感兴趣的架构工程师或硬件开发人员;

• 希望能够快速创建高性能 FPGA 或 ASIC IP 的新项目团队。

研讨会也将介绍更多免费在线 HLS 课程、开源 IP 和其他资源,以便您继续了解 HLS 如何帮助您加速开发 IP 和 SoC。

本文地址:http://mdtic.com/post/30657.html
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!

 发表评论


表情

还没有留言,还不快点抢沙发?