草庐IT

Verilog基础学习二

浅举个栗子 2023-11-26 原文

Verilog基础学习二


文章目录


一、always 块

我们知道数字电路是由导线连接的逻辑门组成,因此任何电路都可以表示为moduleassign语句的某种组合。但是,有时候这不是描述电路最简便的方法。过程块(比如always块)提供了一种用于替代assign语句描述电路的方法。

有两种always块是可以综合出电路硬件的:

综合逻辑:always @(*)
时序逻辑:always @(posedge clk)

1.阻塞性赋值和非阻塞性赋值

在Verilog中有以下三种赋值方法:

连续赋值(assign x=y;):不能在过程块内使用;
过程阻塞性赋值(x=y;):只能在过程块中使用;
过程费阻塞性复制(x<=y):只能在过程块内使用。

在组合always块中,使用阻塞性赋值。在时序always块中,使用非阻塞性赋值。具体为什么对设计硬件用处不大,还需要理解Verilog模拟器如何跟踪事件(译者注:的确是这样,记住组合用阻塞性,时序用非阻塞性就可以了)。不遵循此规则会导致极难发现非确定性错误,并且在仿真和综合出来的硬件之间存在差异。

示例代码如下:

module top_module(
    input clk,
    input a,
    input b,
    output wire out_assign,
    output reg out_always_comb,
    output reg out_always_ff   );

    assign out_assign = a ^ b;
    always@(*) out_always_comb = a ^ b;
    always@(posedge clk) out_always_ff <= a ^ b;
    
endmodule

二、条件语句

1.if 语句 基本用法

基本if语句:

always @(*) begin
    if (condition) begin
        out = x;
    end
    else begin
        out = y;
    end
end

条件(三元)运算符:

assign out = (condition) ? x : y;

condition ? if_true : if_false

2.避免引入锁存器

组合电路输出必须在所有输入的情况下都有值。这意味着必须需要else子句或着输出默认值。

3.case 语句

Verilog中的case语句几乎等同于if-else if-else序列,它将一个表达式与其他表达式列表进行比较。它的语法和功能与C语言中的switch语句稍有不同:

always @(*) begin     // This is a combinational circuit
    case (in)
      1'b1: begin 
               out = 1'b1;  // begin-end if >1 statement
            end
      1'b0:    out = 1'b0;
      default: out = 1'bx;
    endcase
end

1、case语句以case开头,每个case项以冒号结束。而switch语句没有。

2、每个case项只执行一个语句。 *这样就不需要C语言中break来跳出switch。*但这也意味着如果您需要多个语句,则必须使用begin … end。

3、case项允许重复和部分重叠,执行程序匹配到的第一个,而C语言不允许重复的case项目

4.casez 语句

如果case语句中的case项与某些输入无关,就可以减少列出的case项。这就是casez的用途:它在比较中将具有值z的位视为无关项(即输入01都会匹配到)。

还有一个类似的casex,将输入的x和z都视为无关。不认为casex比casez有什么特别的好处。
(作者个人感觉没必要用casex,z和x状态的问题涉及电路的基本知识)
符号"?" 是z的同义词,所以2’bz0与2’b?0相同。

always @(*) begin
    casez (in[3:0])
        4'bzzz1: out = 0;   // in[3:1]输入什么都可以
        4'bzz1z: out = 1;
        4'bz1zz: out = 2;
        4'b1zzz: out = 3;
        default: out = 0;
    endcase
end

同时为避免生成了不必要的锁存器,必须在所有条件下为所有的输出赋值。这可能会多打很多字,使你的代码变得冗长。 一个简单的方法是在case语句之前为输出分配一个“默认值”

除非case语句覆盖赋值,否则这种代码样式可确保在所有可能的情况下输出0。 这也意味着case的default项变得不必要。

提醒:always@(*)综合器会生成一个组合电路,其行为与代码描述的相同。硬件不会按顺序“执行”代码。

三、归约运算符(Reduction Operators)

归约运算符(Reduction Operators)可以对向量的每一位位进行AND,OR和XOR,产生一位输出:

&a [30] // AND:a[3]&a[2]&a[1]&a [0]相当于(a[3:0]== 4'hf)
|b [30] // OR: b[3]|b[2]|b[1]|b [0]相当于(b[3:0]!= 4'h0)
^c [20] // XOR:c[2]^c[1]^c[0]

四、for循环

for循环(组合always块或者generate块)在处理重复动作的时候很有用。

模块实例化时必须使用generate块

 always@(*)begin
        for (int i=0;i<=99;i=i+1)begin
            out[i]=in[99-i];
        end
    end

Problem : Combinational for-loop: 255-bit population count

设计电路来计算输入矢量中 ’1‘ 的个数,题目要求建立一个255bit输入的矢量来判断输入中 ’1‘ 的个数。

module top_module( 
    input [254:0] in,
    output [7:0] out );

    integer i;
    always @ (*)
        begin
            out = 8'b0000_0000;     //**为了后面的计数累加,此处先初始化为0.**
            for (i=0; i<255; i++)
                begin
                    if(in[i] == 1'b1)
                        out = out + 1'b1;
                    else
                        out = out + 1'b0;
                end
        end
endmodule
  • 循环里面的变量一定要初始化

Problem : Generate for-loop: 100-bit binary adder 2

通过实例化100个全加器来实现一个100bit的二进制加法器。该加法器有两个100bit的输入和cin,输出为sum与cout。为了鼓励大家使用实例化来完成电路设计,我们同时需要输出每个全加器的cout。
故cout[99]标志着全加器的最终进位。

  • ​ 有好多加法器需要实例化,可采用实例化数组或generate语句来实现。

​ 考虑到for循环中只有cin与cout是变化的,每次计算中cout是本次计算的输出,也是下次计算的输入(cout就是下次计算的cin)。故我们先计算出cout[0]sum[0]

assign cout[0] = a[0] & b[0] | a[0] & cin | b[0] & cin;
assign sum[0]  = a[0] ^ b[0] ^ cin;
//一定要记得赋初值,然后再开始循环

代码如下

module top_module( 
    input [99:0] a, b,
    input cin,
    output [99:0] cout,
    output [99:0] sum );

    integer i;
    always @(*) begin 
        cout[0] = a[0]&b[0] | a[0]&cin | b[0]&cin;
    	sum[0] = a[0] ^ b[0] ^ cin;
        
        for(i=1;i<100;i++) begin
            sum[i] = a[i] ^ b[i] ^ cout[i-1];
            cout[i] = a[i]&b[i] | a[i]&cout[i-1] | b[i]&cout[i-1];
        end
    end
endmodule

五、Generate 块

什么是generate语句?

​ 生成语句可以动态的生成verilog代码,当对矢量中的多个位进行重复操作时,或者当进行多个模块的实例引用的重复操作时,或者根据参数的定义来确定程序中是否应该包含某段Verilog代码的时候,使用生成语句能大大简化程序的编写过程。

​ 使用关键字generateendgenerate来指定范围。generate语句有generate-for、generate-if、generate-case三种语句,本题中我们使用generate-for语句。

​ generate-for语句:

(1) 必须有genvar关键字定义for语句的变量。

(2)for语句的内容必须加begin和end**(即使就一句)**。

(3)for语句必须有个名字

//创建一个2进制转换器

Module gray2bin
#(parameter SIZE = 8)
(
  input [SIZE-1:0] gray,
  output [SIZE-1:0] bin
)

Genvar gi;  //在generate语句中采用genvar声明
    generate 
  for (gi=0; gi<SIZE; gi=gi+1) 
      begin : genbit    //for语句必须有名字
        assign bin[i] = ^gray[SIZE-1:gi];
      end
    endgenerate 
endmodule

Problem : Generate for-loop: 100-digit BCD adder

​ 本题已经提供了一个名为bcd_fadd的BCD一位全加器,他会添加两个BCD码和一个cin,并产生一个cout和sum。
每个bcd_fadd都是含有两个四位的输入(a,b),一个四位的输出(sum)

module bcd_fadd {
    input [3:0] a,
    input [3:0] b,
    input     cin,
    output   cout,
    output [3:0] sum );

我们需要实例化100个bcd_fadd来实现100位的BCD进位加法器。该加法器应包含两个100bit的BCD码(包含在400bit的矢量中)和一个cin,
输出产生sum 和 cout。

module top_module( 
    input [399:0] a, b,
    input cin,
    output cout,
    output [399:0] sum );

    wire [399:0] cout_temp;
    genvar i;

    bcd_fadd inst1_bcd_fadd (
        .a(a[3:0]),
        .b(b[3:0]),
        .cin(cin),
        .cout(cout_temp[0]),
        .sum(sum[3:0])
    );
    //与上题同理,还是先计算cout[0],我声明一个wire型的cout_temp来存放每次计算后cout的值。
    //类似矢量运算中赋初值,实例的初始化也是必不可少的
    
    generate
        for(i=4; i<400; i=i+4)
            begin: bcd
                bcd_fadd inst_bcd_fadd(
                    .a(a[i+3:i]), 
                    .b(b[i+3:i]), 
                    .cin(cout_temp[i-4]), //上次计算输出的cout
                    .cout(cout_temp[i]), //本次计算输出的cout,在下次计算中变为cin
                    .sum(sum[i+3:i])
                );
            end
    endgenerate 

    assign cout = cout_temp[396];   

endmodule

总结

本次学习了always 块的用法,包含里面的if、case、casez、for语句的具体用法;学习了归约运算符(Reduction Operators)的操作,简化打字量;最后学习了Generate 块的用法,今后在实践中具体操作会有更深刻的理解。

有关Verilog基础学习二的更多相关文章

  1. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  2. 软件测试基础 - 2

    Ⅰ软件测试基础一、软件测试基础理论1、软件测试的必要性所有的产品或者服务上线都需要测试2、测试的发展过程3、什么是软件测试找bug,发现缺陷4、测试的定义使用人工或自动的手段来运行或者测试某个系统的过程。目的在于检测它是否满足规定的需求。弄清预期结果和实际结果的差别。5、测试的目的以最小的人力、物力和时间找出软件中潜在的错误和缺陷6、测试的原则28原则:20%的主要功能要重点测(eg:支付宝的支付功能,其他功能都是次要的)80%的错误存在于20%的代码中7、测试标准8、测试的基本要求功能测试性能测试安全性测试兼容性测试易用性测试外观界面测试可靠性测试二、质量模型衡量一个优秀软件的维度①功能性功

  3. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  4. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  5. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  6. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  7. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  8. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  9. 深度学习12. CNN经典网络 VGG16 - 2

    深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG

  10. 【网络】-- 网络基础 - 2

    (本文是网络的宏观的概念铺垫)目录计算机网络背景网络发展认识"协议"网络协议初识协议分层OSI七层模型TCP/IP五层(或四层)模型报头以太网碰撞路由器IP地址和MAC地址IP地址与MAC地址总结IP地址MAC地址计算机网络背景网络发展        是最开始先有的计算机,计算机后来因为多项技术的水平升高,逐渐的计算机变的小型化、高效化。后来因为计算机其本身的计算能力比较的快速:独立模式:计算机之间相互独立。    如:有三个人,每个人做的不同的事物,但是是需要协作的完成。    而这三个人所做的事是需要进行协作的,然而刚开始因为每一台计算机之间都是互相独立的。所以前面的人处理完了就需要将数据

随机推荐