7天搞定FPGA精录&总结Episode.1 认识工具,掌握基础【基于Robei及VerilogHDL】

芯片是我国的痛,尤其是这几年。最近有段时间坐下来静静思考这个问题,有些想法,所以开篇P1的引言稍微要长一些。

我起初在布克书店看书的时候,也完全没有敢想过七天学会FPGA这个东西。之前我们的课程上也布置了一些写代码的作业,基本上是一个头顶两个大的状态。所谓七天搞定FPGA这和七天挣他一个亿有什么区别。

回家认认真真学习了之后才明白,想要快速熟知FPGA是不现实的。但是想要快速入门FPGA并不是一件难事,前提是需要具备熟练使用C语言的基础,对数电的东西有所了解,以及会用复杂的电脑软件。这个复杂只是“思维模式“上的复杂,而不是指占用几十个G这种空间上的庞然大物。

说到这里,便要看看EDA这个骚玩意儿了。学校寄送给我们的EGO1开发板,搭载Xilinx Artix-7芯片,基于Vivado开发软件,除此以外比较热门的还有Xilinx六系列及之前的ISE开发软件、已经被Intel收购的Altera旗下的QuartusⅡ软件等等,它们使用的都是VerilogHDL或者VHDL语言进行硬件开发。最近两年因为国际形势的日趋紧张,尤其是中美关系问题,美国已经逐渐在对我们限制这些高新技术工具的使用权力。再加上每个公司的芯片和EDA存在严重的IP核垄断,如果美国卡死这条设计通路,必然会让国内的芯片研发工作陷入僵局。虽然美国离不开我们这个市场,但是我们更离不开他们的这些我们现在还刚不住的技术资源。

众所周知芯片开发需要经过光刻机、蚀刻机等顶级精密程度的仪器。蚀刻机方面上海的中微已经在研发和投产3nm精度的产品,技术已经接近世界前列。但是光刻机方面还差得远。荷兰ASML龙头大佬已经在1991年就研发投用了90nm光刻机,遗憾的是90nm是目前我国光刻的最高技术精度。最近一年特朗普政府也在鼓吹荷兰方面限制ASML公司对中国的14nm及以下的光刻机(EUV光刻机)的技术支持。硬件和软件支持两面受敌,这也是为什么国家这些年投入如此大的人力物力搞芯片,没有它我们得回到手写时代(虽然有点夸张,但是并不虚假)。

谈了这么多,解决方法确是非常的简单,四个字即可概括:支持国产!

就我所知,位于广东的Gowin高云半导体有生产FPGA,Sipeed矽速科技用其做开发板。价格只要五美元的Tang-Nano FPGA开发板就是出自他们之手,而且其功能足够满足我们本科期间的一切开发工作。于是我果断入手,而且也很有幸在电邮告知了我的身份后,他们愿意免费为我提供EDA工具的使用权限。除此以外,相同经历的还有青岛若贝电子开发的Robei EDA,仅有10M大小却能兼容世界上两霸(X、A)的所有芯片同时进行图例代码二合一开发的神器。这也将是我总结的这个专栏系列所用到的主要EDA软件。

总而言之,与其费尽口舌争论疫情期间暴露的国内外政策优劣和关系亲疏问题,不如给国产打打广告。我相信天才的演说家背后一定有千千万万的技术人才或团队在包装,没有点硬实力什么东西都是空谈的。我认为这也是目标科学工作的年轻人当下应当有的价值观。


虽然才刚刚兴起没多少年,但是市面上教芯片设计、FPGA设计开发的教材资料层出不穷。实际上经过大学阶段的学习,我们深知尽管这些教材的编写者都是受人尊敬的顶级专家和前辈,但是不少内容均有一厢情愿侃侃而谈之嫌,专业但是不用户友好。这就和那些如同Xilinx Vivado一样几千人开发、升级然后占用存储空间高达40个G的开发软件一样,对即使是接触了很长一段时间的小型数字电路、CPLD和FPGA开发者来说都尚且不友好。于是我们需要一些简单的东西。首先,用入门者的眼光来看,什么才是FPGA?

FPGA出现之前,所有集成电路都可以看成雕塑家,但是雕成一个成品,往往要浪费很多半成品和原料,这就是ASIC的制造。

后来FPGA出现了,FPGA就是块橡皮泥,什么硬件电路都能模仿,万用IC,想捏成什么样随你,捏的不行,可以重新再捏。这就是为什么被称为可编程。

也可以把FPGA当做乐高积木。

比如商场里现成的玩具模型,小汽车、城堡等,这些买来就可以玩,是厂家给你做好的。喜欢什么就买什么,买了四个轮子的小汽车,发现四个轮子不好玩,其实想要三轮车,这就没办法,你只能再去掏钱买。——这相当于ASIC;

买了一台游戏机,玩什么游戏另外插卡。没有游戏卡,就是废铁。——这就相当于CPU或者ARM。

FPGA相当于乐高积木,买来的是以大堆零件(FPGA里的IOB、SLICE、blockram等),车轮、屋顶这些零件集成度很高(相当于FPGA里的DCM、DSP等);玩家根据图纸,可以搭出多种样式的模型。

以英特尔FPGA为例(ALTERA),生产各种芯片,综合工具Quartus II,包括设计输入,时序仿真,板上验证,大部分的功能都集成了。Quartus II就是捏橡皮泥的工具。

Nios II,是Altera公司推出的FPGA用于嵌入CPU软核的支持软件,用C、C++都可以写。所谓软核,就是以前大家都用FPGA捏ASIC玩,但是FPGA常常要外接一些处理器,ARM、MCU,于是Altera设计了个软核出来,FPGA以前模仿硬件电路,现在连处理器都能模仿了,变成万用橡皮泥了。Nios II就是用来捏这块万用橡皮泥以前不能捏的那部分。

这里要致谢参考了@FPGA极客空间的部分鞭辟入里的文章,以下附上高清大图几张,感受一下传说中【可编程门阵列芯片】的曼妙身姿:

7天搞定FPGA精录&总结Episode.1 认识工具,掌握基础【基于Robei及VerilogHDL】_第1张图片

7天搞定FPGA精录&总结Episode.1 认识工具,掌握基础【基于Robei及VerilogHDL】_第2张图片

7天搞定FPGA精录&总结Episode.1 认识工具,掌握基础【基于Robei及VerilogHDL】_第3张图片

7天搞定FPGA精录&总结Episode.1 认识工具,掌握基础【基于Robei及VerilogHDL】_第4张图片


Robei的安装和使用总结部分:

安装方法略去,百度到官网上下载即可,正常安装就好,如果是大学生的话可以考虑电邮或者官网注册那边申请免费使用权限,想必出于学习目的开发者们是会同意的。Robei的最大优势就是体积小、图形化和代码编写二合一的方式,能从图形创建的模块直接生成好代码框架,再往里面填写具体内容。这个功能比较新颖实用,而且省时间,我们甚至可以在安卓手机、U盘带到公用电脑上去随心所欲开发FPGA(P.S.博主提醒:注意保密哦)。

另外的话Robei进行开发过程不支持VPN,请使用国内网或者国内服务器进行网络连接,诸如芯片引脚选型这些操作是需要连接内网才能使用的。

 

一、Robei程序界面的五大板块:

1、菜单和工具条,位于上端,可进行一系列的基础操作,包括Run simulation、Windows调整、复制粘贴、Tool中添加模块引脚连接线这些常用操作

2、工具箱:两栏工具箱在左侧,Current对应当前工作文件夹,System包含已经在软件内部开发/集成好的一些模块样板,拿出来就能用

7天搞定FPGA精录&总结Episode.1 认识工具,掌握基础【基于Robei及VerilogHDL】_第5张图片

3、属性栏:位于右侧,显示信息。具体的部分数据有些受到保护,不能更改,只能查看

7天搞定FPGA精录&总结Episode.1 认识工具,掌握基础【基于Robei及VerilogHDL】_第6张图片

4、工作空间:正中大白板,利用下述的Robei三元素进行开发的实际操作部分,同时也是写代码的位置

7天搞定FPGA精录&总结Episode.1 认识工具,掌握基础【基于Robei及VerilogHDL】_第7张图片

5、输出栏:显示输出信息以及错误和警告

 

二、Robei三元素:

1、模块:简答来说就是一个抽象的芯片框以及其包含的内部所有硬件架构

①Module:编写设计过程中的模块

②Model:设计好的模块或者正在被调用的其它模块

③Testbench:测试模块,用于生成激励调用设计好的模块进行仿真验证等操作

④Constrain:约束,其中包括管脚约束等等,约束的正常生成需要通过Setting-FPGA设置里面正确选择FPGA的厂家和型号

2、引脚:模块进行I/O操作的口,任何模块不会是光杆司令,一定需要外部信号的输入和处理后信号的输出(或者二者有其一)。进行输入和输出的一个连接口我们称作引脚。

3、连接线:连接两个引脚的工具,负责信号的传输

P.S.以上三元素均可以通过代码也可以通过图形化界面完成,二者相辅相成效率和准确度是最高的。


VerilogHDL的基础语法总结部分,此处最好先复习C语言的语法和程序设计思路相关知识(感谢参考工程师@shawn233的部分总结):

一、模块(module):

模块是verilog最基本的概念,是verilog设计中的基本单元,每个verilog设计的系统中都由若干module组成。用module和endmodule关键词来标记模块的开始和结束,在module和endmodule之间的代码都是属于本模块的。模块名就是当前文件名。

1、模块的实际意义是代表硬件电路上的逻辑实体。

2、每个模块都实现特定的功能。

3、模块之间是并行运行的。

4、模块是分层的,高层模块通过调用、连接低层模块的实例来实现复杂的功能。

5、各模块连接完成整个系统需要一个顶层模块(top-module)。

二、变量及符号:

1、Verilog区分大小写

2、Verilog的关键字都是小写

3、parameter关键字定义一个参数,增强模块的通用性

4、output, input关键字指定输入输出

5、always @(posedge/negedge clk)语句,每当clk出现上升/下降沿时,就执行接下来的语句块(begin, end标志一个语句块)。这种语句构成时序逻辑,当前输出与上一个时刻的值有关

6、reg类的变量用<=赋值,赋值符号左侧是寄存器,右侧是更新的值

7、&q语句输出q每一位相与的结果,即&q=q[1]&q[2]&...&q[N]

8、assign关键字描述组合逻辑,输出只与当前输入有关

9、input/output如果没有显示声明类型,默认为wire类型。如果需要reg类型以存储值,需要显示声明

10、wire类型只是一根导线,只能用来组合逻辑。assign语句等号右侧一旦发生变化,等号左侧立刻得到结果;而always语句块中的值改变只能发生在每一时刻(由@符号后的语句描述时刻)

11、initial begin, end语句块指明初始化语句,这部分指令在系统上电后直接执行

12、'timescale 1ns/1ps指定测试的单位是1ns,精度是1ps

13、#100指令指明延时,单位由之前的代码指定

14、$stop指令使仿真停止

15、时序逻辑:<=赋值;组合逻辑:-赋值

三、运算符:

算术运算符(+,-,X,/,%)

赋值运算符(=,<=)

关系运算符(>,<,>=,<=)

逻辑运算符(&&,||,!)//与或非

条件运算符(?:)

位运算符(~,|,&,^,^~)//取反,按位或,按位与,按位异或,按位同或

移位运算符(<<,>>)

拼接运算符({  })

四、语句:

1、always过程块:

①always语句块不能嵌套,因为它们全部是并行执行的

②在敏感信号表达式前加posedge或negedge关键字可以指定上升沿或下降沿触发。否则每当表达式的值发生改变,就会执行语句块

2、initial过程块:

①initial过程块模拟上电之后的行为,不可综合,通常用于功能模拟的初始化,写在测试文件(testbench)中

②同一模块中的initial过程块,在上电时并行执行

③initial过程块不能嵌套,因为其只在0时刻执行一次

3、assign连续赋值:

①assign语句常用于对wire类型变量进行赋值

②等式左边的wire变量随等式右边的值一起变化

4、阻塞与非阻塞赋值见下图总结:

7天搞定FPGA精录&总结Episode.1 认识工具,掌握基础【基于Robei及VerilogHDL】_第8张图片

五、if、case、for、while等条件或者循环语句:

一个简单的记忆方法是,它们与C中的对应字符功能和写法基本完全相同。唯一需要改变的一点是,C语言中对应语句如果要使用大括号{},在Verilog中把括号首尾分别改称单词begin和end。此外:

1、while在C语言中有好几种格式,在Verilog中一般只用开头定义判定式的格式,同时一定是先判定再执行语句内的操作

2、Verilog里面没有自加语句i++,所以for中循环字符i增加需要用i=i+1的表达式来写


语法的总结就是这样简洁。与此同时下面给出了一个代码模型可以巩固复习Verilog几个重要基础语法的内涵。

//基础并行语句与if条件判断的示例
module a(b, c, d,...z);//module: 模块头 a:模块名 (b,c,d,...z):端口列表
    input b;//输入声明
    input wire c;//输入声明线网类型用wire,wire可省略
    input wire [7:0] d;//[7:0]:输入总线位宽0~7所以是8bit总线
    output e;//输出声明
    output [7:0] f;//输出总线位宽说明,默认为wire类型,此处省略wire
    output reg [7:0] f;//输出总线寄存器类型用reg
   ...//为了篇幅小一些省略e~y的声明,实际代码中不可省略
    assign d = a & b;//assign语句也叫数据流建模语句也叫连续赋值语句,后面接的是组合逻辑
    assign e = (f < g)?  1 : 0;//三目运算符
    
    always @ (posedge a or negedge b or posedge c...)//always语句,posedge为上升沿触发,negedge为下降沿触发,后面接信号表示当这个信号上升沿或下降沿时执行下面的程序
        begin //begin...end 相当于()
            if(!b)//if语句
                begin
                    h <= 4'b0000;//非阻塞赋值语句用<=,4'b0000表示位宽为4,二进制表示的数0000
                    i <= 32'haabbccdd;//此处表示32位宽,16进制表示的数aabbccdd
                end
    
            else//else表示分支
                case(j)//case语句
: k <= k + 1'b1;//verilog中没有自加的表示,所以用k = k + 1'b1
: if(k
//四选一多路选择器
module MUX41a(a,b,c,d,s1,s0,y);
input a,b,c,d;
input s0,s1;
output y;
 
reg y;//变量有两种,寄存器类型(reg)和线型(wire),没有特意定义的,一般默认为wire类型
//只能对寄存器类型端口赋值
 
always@(a,b ,c ,d ,s1 ,s0)//必须列入所有输入端口
  begin : MUX41
    case({s1,s0})
        2'b00: y<=a;
        2'b01: y<=b;
        2'b10: y<=c;
        2'b11: y<=d;
        default: y<=a;
    endcase
  end 
//case是一种全等比较,必须每一位都相同
//casez语句用来处理不考虑高阻态z的比较过程
//casex语句用来处理不考虑高阻值Z和不定值x的情况
//D触发器示例
module DFF1 (CLK,D,Q);
   output Q;
   input  CLK,D;
   reg Q;
   always@ (posedge CLK)
      Q<=D;
endmodule
/*时序模块的简单识别,
  1、always的敏感信号列表含有posedge或者negedge
  2、always语句中有不完整的条件语句,例如if(CLK) Q
//含异步清零和时钟同步使能的D触发器
module DFF2 (CLK,D,Q,RST,EN);//同步,依赖时钟;异步,不依赖时钟
   output Q;
   input CLK,D,RST,EN;
    always@(posedge CLK or negedge RST)
    //异步清零,该复位信号出现在always中,与时钟在一起,说明不依赖时钟,为异步
      begin
        if(!RST) Q<=0;
        else if(EN) Q<=D;
      end
endmodule
//有异步复位,同步计数使能和可预置型十进制计数器
module CNT10 (CLK,RST,EN,LOAD,COUT,DOUT,DATA);
    input CLK,EN,RST,LOAD;
    input [3:0] DATA;//4位并行加载数据
    output [3:0] DOUT;//计数数据输出
    output COUT;
    reg [3:0] Q1; reg COUT;
    assign DOUT=Q1;//将内部寄存器的计数结果输出至DOUT
    always@(posedge CLK or negedge RST) begin
            if(!RST) Q1<=0;//异步清零
            //RST下降沿时,清零,
            //之后,若RST一直低电平,则一直清零状态
            //当CLK上升沿,并且RST=1,EN=1时,看LOAD的值
            //LOAD=0时,置数,LOAD=1时,累加1
       else if(EN) begin //同步使能EN=1,此时允许加载或计数
                 if(!LOAD) Q1<=DATA;//当LOAD=0,向内部寄存器加载数据
            else if(Q1<9)  Q1<=Q1+1;//当Q1小于9时,允许累加,此时为十进制计数器
            else Q1<=4'b0000; end
            end 
    
    always@ (Q1)//组合逻辑电路
        if(Q1==4'h9) COUT=1'b1;  //当Q1=1001时,COUT输出进位标志1
           else     COUT=1'b0;  //否则,输出进位标志0
endmodule
//移位模式可控的8位移位寄存器
module SHFT2(CLK,C0,MD,D,QB,CN);
    output CN;//进位输出
    output [7:0] QB;//移位数据输出
    input [7:0] D;//待加载移位的数据输入
    input [2:0] MD;//移位模式控制字
    input  CLK,C0;//时钟和进位输入
    reg [7:0] REG; reg CY;
    
  always@(posedge CLK) begin
    case(MD)
    1:begin REG[0]<=C0;REG[7:1]<=REG[6:0]; CY<=REG[7];end //带进位循环左移
    2:begin REG[0]<=REG[7]; REG[7:1]<=REG[6:0]; end //自循环左移
    3:begin REG[7]<=REG[0]; REG[6:0]<=REG[7:1]; end //自循环右移
    4:begin REG[7]<=C0; REG[6:0]<=REG[7:1]; CY=REG[0]; end //带进位循环右移
    5:begin REG<=D; end //加载待移数
    default:begin REG<=REG;CY<=CY;end //过程结束
    endcase end
      assign QB=REG;//移位数据并行输出
      assign CN=CY;//左移高位输出
endmodule
//同步FIFO
module sc_fifo(
input clk,rst,

input [7:0] din, 
input wr,
output full,

output reg [7:0] dout,
input rd,
output empty
);

//1,需要一个buff双口存储器做存储载体,当地址有n位,就有2^N个存储单元,标号从0到(2^N-1)。三位地址存储地址就是从0到7。
reg [7:0] buff[0:7];

//2,需要wr_ptr指针,与地址位数相同,指向下个可以写的地址。
reg [2:0] wr_ptr;
//3,需要rd_ptr指针,与地址位数相同,指向下个可以读的地址。
reg [2:0] rd_ptr;
//4,需要fifo_cntr计数器,比地址位数多一位,指示当前可读的数据个数。
reg [3:0] fifo_cntr;
//5,full和empty由fifo_cntr生成。
assign full=fifo_cntr==8;
assign empty=fifo_cntr==0;
//6,区别读写操作是否有效。
wire valid_rd=~empty &rd;
wire valid_wr=~full≀
//7,实现以上功能
always@(posedge clk) if(rst) wr_ptr<=0;else if (valid_wr) wr_ptr<=wr_ptr+1;
always@(posedge clk) if(rst) rd_ptr<=0;else if (valid_rd) rd_ptr<=rd_ptr+1;

always@(posedge clk) if(rst) fifo_cntr<=0;else
if ((valid_rd==0)&&(valid_wr==1)) fifo_cntr<=fifo_cntr+1;
else if ((valid_rd==1)&&(valid_wr==0)) fifo_cntr<=fifo_cntr-1;
/*用case语句写
always@(posedge clk) 
casex ({rst,valid_wr,valid_rd})
3'b1xx : fifo_cntr<=0;
3'b010 : fifo_cntr<=fifo_cntr+1;
3'b001 : fifo_cntr<=fifo_cntr-1;
3'b011 ,3'b000 :fifo_cntr<=fifo_cntr ;
endcase  */


always@(posedge clk) if(valid_wr) buff[wr_ptr]<=din;
always@(posedge clk) if(valid_rd) dout<=buff[rd_ptr];

endmodule

由于今天只说了有关基础语法入门的东西,没有总结到具体设计的内容,所以无需完全掌握上面几种简单功能实现的具体思路。只需要依葫芦画瓢能够熟练运用VerilogHDL基础语法和用Robei EDA工具进行编写即可。

你可能感兴趣的:(7天搞定FPGA精录&总结)