• 【深入浅出玩转FPGA学习2----设计技巧(基本语法)】


    基本语法(可综合的Verilog语法子集)

    所谓可综合的Verilog语法,是指硬件能够实现的一些语法。常用的RTL语法结构如下:
    模块声明:module…endmodule.
    端口声明:input,output,inout(inout的用法比较特殊,需要注意)。
    信号类型:wire,reg,tri等,integer常用于for语句中(reg,wire是最常用的,一般tri和integer用在测试脚本里)。
    参数定义:parameter。
    运算操作符:各种逻辑操作符、移动操作符、算数操作符大多是可综合的(注:=== 与 !==是不可综合的)
    比较判断:if…else,case(casex,casez)…default…endcase.
    连续赋值:assign,问号表达式(?:)。
    always模块:敏感表可以为电平、沿信号posedge/negedge;通常和@连用。
    begin…end (通俗的说,它就是C语言里的“{}”)。
    任务定义:task…endtask.
    循环语句:for(用的也比较少,但是在一些特定的设计中使用它会起到事半功倍的效果)
    赋值符号:=和<=(阻塞和非阻塞赋值,在具体设计中是很有讲究的)。
    可综合的语法是Verilog可用语法里很小的一个子集,硬件设计的精髓就是力求用最简单的语句描述最复杂的硬件,这也正是硬件描述语言的本质。

    If…else 与 case 语句分析

    两者结构完全一致的情况

    两段代码,EX1使用if…else语句,EX2使用case语句。

    //EX1
    input clk;
    input rst_n;
    input[3:0] data;
    output[2:0] add;
    reg[2:0] add;
    always @(posedge clk) begin
    	if(!rst_n) begin
    		add<=0;
    		end
    	else begin
    		if(data<4) add<=1;
    		else if (data<8) add<=2;
    		else if(data<12) add<=3;
    		else add<=4;
    		end
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    // EX2
    input clk;
    input rst_n;
    input[3:0] data;
    output[2:0] add;
    reg[2:0] add;
    always @(posedge clk) begin
    	if(!rst_n) begin
    		add<=0;
    		end
    	else begin
    		case(data)
    		0,1,2,3: add<=1;
    		4,5,6,7: add<=2;
    		8,9,10,11: add<=3;
    		12,13,14,15: add<=4;
    		default: ;
    		endcase
    		end
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述
    在这里插入图片描述
    图2.1和图2.2分别是if…else 语句和case 语句综合后的RTL视图。 单从RTL视图来看,二者综合后的结果是有明显区别的。if…else趋向于有优先级的结构,而case则是并行的结构。
    接下来看看它们所占用的资源情况。二者资源占用的情况基本是完全一样,连平均扇出也一致。
    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    再看它们的Technology Map Viewer,分别如图2.3和图2.4所示。二者完全一致,所以,可以明确的说,在这个例子中,if…else和else 语句最终的实现都是并行的,而且完全一致。
    在这里插入图片描述

    两者结构不一样的情况

    // if...else 示例代码
    input clk;
    input rst_n;
    input close ,wr, rd;
    output[2:0] db;
    reg[2:0] dbr;
    always @ (posedge clk or negedge rst_n) begin
    	if(!rst_n) begin
    		dbr<=3'd0;
    		end
    	else begin
    		if(close) dbr <=3'b111;
    		else if(rd) dbr <=3'b101;
    		else if(wr) dbr <=3'b011;
    		else dbr <= 3'd0;
    	end
    end
    assign db =dbr;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    // case 示例代码
    input clk;
    input rst_n;
    input close ,wr, rd;
    output[2:0] db;
    reg[2:0] dbr;
    always @ (posedge clk or negedge rst_n) begin
    	if(!rst_n) begin
    		dbr<=3'd0;
    		end
    	else begin
    		case({close,rd,wr})
    			3'b100:dbr <= 3'b111;
    			3'b010: dbr <=3'b101;
    			3'b001: dbr <=3'b011;
    			default:dbr <= 3'do;
    			endcase
    	end
    end
    assign db =dbr;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    对于上面两段代码,单从代码上分析,if…else是带优先级的,case是平行结构。
    二者的资源消耗是存在差异的,那么二者的最终实现也不一定是不一样的。
    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
    从RTL视图看,二者的实现确确实实也正如早先所预期的,一个带优先级,一个并行处理。
    在这里插入图片描述
    从最终的布局布线后的结构看,和RTL视图很接近,这两个示例代码所使用的if…else和case最终实现的结构是有差异的。
    从之前的实力分析看这并不稀奇,意外的是使用if…else实现的结构资源消耗居然比case要来的少(只是相对而言)。这样的结果似乎能够很好的反驳不少人提出的所谓“多用case语句,少用if…else语句,因为实现带优先级的结构比并行结构更耗费资源”的论断。
    在这里插入图片描述
    另外,该例子中使用多个if…if…语句实现的结果会和case语句的结果一致。

    // if...if.... 示例代码
    input clk;
    input rst_n;
    input close ,wr, rd;
    output[2:0] db;
    reg[2:0] dbr;
    always @ (posedge clk or negedge rst_n) begin
    	if(!rst_n) begin
    		dbr<=3'd0;
    		end
    	else begin
    	dbr <=3'd0;
    	if({close,rd,wr}==3'b100) dbr<=3'b111;
    	if({close,rd,wr}==3'b010) dbr <=3'b101;
    	if({close,rd,wr}==3'b001) dbr <=3'b011;
    	end
    end
    assign db =dbr;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Verilog 代码优化之for语句

    Verilog中的for语句虽然也是可综合的,但在RTL级的代码中基本不用。一方面是因为for语句的使用是很占用硬件资源的,另一方面是因为在设计中往往是采用时序逻辑设计,用到for循环的地方不多。
    下面用一个实例说明for循环语句综合实现的结果。

    module test_3(clk,rst_n,data,num);
    input clk;
    input rst_n;
    input[12:0] data;   //输入13路数据
    output[15:0] num;   //13路数据电平为高的路数
    reg[3:0] i;
    reg[15:0] num;
    always @ (posedge clk) begin
    	if(!rst_n) begin
    		num <=0;
    		end
    	else begin
    		for(i=0;i<13;i=i+1)     //用for循环进行计算
    			if(data[i]) num<= num+1;
    		end
    	end
    	endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这段代码的用意是在一个时钟周期内计算出13路脉冲信号为高电平的个数,一般人都会感觉这个任务交给for循环来做再合适不过了,但是for循环不能够完成这个任务。
    在这里插入图片描述
    相信你已经发现问题了,为什么每个时钟周期for循环只执行以此num<=num+1呢?always 语句中使用非阻塞赋值“<=”时,是在always 结束后才把值赋给左边的寄存器,因此才出现了上面的情况。重新用阻塞语句写了如下代码:

    module test_3(clk,rst_n,data,num);
    input clk;
    input rst_n;
    input[12:0] data;   //输入13路数据
    output[15:0] num;   //13路数据电平为高的路数
    reg[3:0] i;
    reg[15:0] num;
    always @ (posedge clk) begin
    	if(!rst_n) begin
    		num =0;
    		end
    	else begin
    		for(i=0;i<13;i=i+1)     //用for循环进行计算
    			if(data[i]) num = num+1;
    		end
    	end
    	assign numout = num;
    	endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    重新修改后的代码进行仿真的波形如图所示:
    在这里插入图片描述
    此波形说明了修改后的代码达到了实验目的。看来for语句在这种情况下还是比较省事的,如果不用for语句就比较繁琐了。但是话说回来,for语句综合的效率不高,在对速度要求不高的前提下,还是宁愿用多个时钟周期去实现也不用for语句。
    此外,一般在时序逻辑里多用非阻塞赋值语句"<=",像后面一个代码的风格其实是不可取的。硬件语言不能像C语言一样片面地追求代码的简洁。

    inout用法

    如表所示,它指明了在同等驱动强度下,两个驱动的wire型和tri型变量的真值表。
    在这里插入图片描述
    如果某时刻inout端口有输入,此时又正好要拿这个inout端口做输出,那么冲突是在所难免的,会出现什么样的结果可以参考真值表。
    下面是一种典型的inout端口的使用方法:

    inout io_data;     //inout口
    reg out_data;    //需要输出的数据
    reg io_link;        //inout口方向控制
    assign io_data = io_link ? out_data : 1'bz;   //这个是关键
    
    • 1
    • 2
    • 3
    • 4

    当inout端口作为输入口使用时,一定要把它置为高阻态,让例子中的io_link=0即可;当inout端口作为输出口使用时,则将实例中的io_link =1,对out_data赋值就可以了。

  • 相关阅读:
    5G与UWB定位技术融合的四种方式
    bp神经网络图像特征提取,神经网络提取特征值
    【重识云原生】第四章云网络4.7.2节——virtio网络半虚拟化简介
    MindFusion.Diagramming for WPF V3.8.3
    2021年全国职业院校技能大赛-ruijie网络模块-命令解析-脚本配置
    薄膜和涂层中应力产生和松弛的机理
    MT4深受投资者喜欢,anzo capital昂首资本认为这几个特点必不可少
    简化的 Java 六边形架构
    你的Linux进阶之旅,终点是何处?
    手撕无头单链表
  • 原文地址:https://blog.csdn.net/qq_38617667/article/details/125490129