在老板的要求下,我开始学习接触FPGA相关内容。而我们所用到的FPGA综合开发软件为vivado,虽然还没练习时长两年半,但也有一定的经验,接下来我把学习中遇到的问题记录如下,希望能帮助到刚入门的萌新。如果有一定的语言基础(例如c、matlab、Python等等),则搞懂以下问题,对于verilog语言的学习就会非常简单。
1.1 基本代码结构
在创建工程项目后,代码页面会自动生成如下语句(工程的创建及测试testbench等博主闲了再出教程敷衍大家)
module csdn(
//括号内为端口列表
);
//此处为I/O说明
//内部信号声明(也可理解为中间变量声明)
//主要的代码功能定义
endmodule
我们的代码内容写在module…endmodule中,对于括号内应进行输入输出端口的定义,该定义和测试testbench中相对应。
括号外则进行变量的声明及相关代码的撰写。(具体内容如下例1.2)
1.2 简例
1.2.1 简单的与或非运算
module csdn(
input a,
input b,
output [5:0] y
//此处定义三个端口,a,b为两个一位的输入端口,y为一个六位的输出端口
//未定义位数则自动按一位
);
assign y[0] = a & b;
assign y[1] = ~(a & b);
assign y[2] = a | b;
assign y[3] = ~(a | b);
assign y[4] = a ^ b;
assign y[5] = a ~^ b;
//此处assign声明语句
//此处为a,b的与 或 非 异或运算赋值到y的各个位中
endmodule
此程序为我接触verilog语言学习的第一个程序,很适合新手去当做入门仿真的练习。接下来还有一个程序,则为我的项目所涉及到的部分代码,此程序更偏向于应用,且更符合第一部分介绍的基本代码结构
1.2.2 线性反馈移位寄存器(此部分可在看懂always和begin后详细了解)
如下图,是为了方便讲解而手绘的简易六位线性反馈移位寄存器。其寄存器在每个时钟周期右移一位,即寄存器R0的1-5位移到2-6位,寄存器R0的第一位赋值为反馈值,该反馈值为寄存器第三位和第六位的异或值。其第六位在每个周期输出一次,该值记为code。其输入初值为L1C,该初值在程序开始时对寄存器R0进行赋值。
该线性反馈移位寄存器实现代码如下:(预告:接下来的程序测试testbench可能用这个程序来讲解)
module L1C(
clk,
work,
l1c,
code
//端口列表
);
input clk;
input work;
input [5:0]l1c;
output code;
wire code;
//I/O端口说明
//此处的input output也可以写到括号内,看个人习惯,我习惯括号内声明端口,然后再去对I/O进行具体的说明
reg [5:0]r0; //寄存器RO
wire feedback; //反馈值
//中间变量定义
// -----------------------------------------------------------------------------------------------------
// ------------------------------Code Generator Part ---------------------------------------------------
// -----------------------------------------------------------------------------------------------------
always @(posedge clk) //此语句用法下文会讲,在这里主要看代码的结构
begin
if(!work)
begin
r0 <= l1c;
//对寄存器r0赋初值,初值l1c在testbench中会讲到
end else
begin
r0 <= {r0[4:0],feedback};
//此处为上文讲解的移位和反馈操作
end
end
assign feedback = r0[2] ^ r0[5]; //反馈值
assign code = r0[5]; //输出值
endmodule
此程序如果有一定的基础,应该很简单,如果没有基础,则可以忽略其中的语法,继续往下看,下面将具体讲解其中always和begin…end的用法,而这里主要看代码的结构,其结构主要包括的即为1.1中所介绍的。
注:程序中的input值在testbench中输入。对于c、matlab中一般都直接赋值,或者通过读取指令进行赋值。此处可当做其需要读取,但其实际赋值过程在testbench中进行。
2.1 always 常用结构
此语句对于初学者可能是个麻烦,但其所完成的操作其实很简单,相当于一个判断语句,其可类比我们所熟悉的if语句,在if()语句中,其括号内的值为1(真)则执行其下面的语句。而对于always而言,其主要针对的是时钟,在硬件中,一般通过设置时钟进行操作,always则是判断你所设置的时钟,在时钟的上升沿或者下降沿进行其下面的语句。
always @()
对于always而言最常用到的莫过于下面语句
always @(posedge clk or negedge rst)
//异步复位 clk上升沿,rst下降沿 满足其中一个条件即开始执行
begin
end
具体clk及rst的值可在测试端testbench中设定,testbench后续会教大家怎么创建
3.1 begin…end 用法解读
begin…end其实很好理解,在此语句中的程序顺序执行(begin语句算是verilog语言的一个习惯,在每个执行的语句前加上,养成好的习惯)
如果没有begin…end则对于多个语句而言,只会执行一个(相当于一个框,对于判断语句而言,框到的地方即为需要执行的地方)
具体如下
always @(posedge clk) //通过判断clk上升沿执行以下操作
begin
r0 <= l1c;
r1 <= l1c;
end
//在此语句中r0和r1的值都会随着l1c在不同的clk上升沿值变
always @(posedge clk)
r0 <= l1c;
r1 <= l1c;
//如果没有begin...end限制,则通过always判断后,只执行其下面一个语句
//当然在实际操作中这样肯定会报错,博主想表达的意思为,没有begin...end限制,其r1 <= l1c就不属于always中的语句了
//而普通的等式则需要用assign去定义,r1 <= l1c则可变成assign r1 = l1c
3.2 简例
我们用到的程序是1.2.2中我自己写的一个比较简单的反馈移位寄存器具体操作部分
always @(posedge clk)
//clk上升沿执行always对应的下面begin...end中的语句
begin //此处begin对应最末尾的end
if(!work) //如果work为0,则开始执行其begin...end中的语句,对r0进行赋值
begin
r0 <= l1c;//初值l1c在testbench中会讲到
end else //如果work为1则执行else下面的begin语句
begin
r0 <= {r0[4:0],feedback};//此处为移位反馈操作
//r0的0-4位放在高位,r0的最低位为feedback值
end
end
每个begin对应一个end,相距最近的begin和end为一对,其程序的执行在一对begin…end中顺序执行文章来源:https://www.toymoban.com/news/detail-448406.html
总结
硬件语言和软件语言大体差不多,如果认真学习过一门语言,我觉得想上手其他的语言将会非常简单,只是其定义和语法可能有一些差异。我对于verilog语言的学习源自于老板直接扔给我几千行的代码让我看,其代码很好懂,但仿真折磨了我许久。之后我会继续把仿真testbench过程做一个详细的讲解,但在仿真之前最好把1.2.2的线性反馈移位寄存器看懂(虽然不看懂也没啥影响,看懂的话可以明白其仿真中各个信号波形的含义)文章来源地址https://www.toymoban.com/news/detail-448406.html
到了这里,关于Verilog基本代码结构及常用语句always、begin...end解读的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!