02-初识Verilog
阅读原文时间:2023年07月26日阅读:1

1.开发环境搭建

需要使用的软件:

  • QuartusII
  • ModelSim
  • Visio
  • Notepad++

2.初识Verilog

  • Verilog HDL是一种硬件描述语言,以文本形式来描述数字系统硬件的结构和行为的语言,用它可以表示逻辑电路图\逻辑表达式,还可以表示数字逻辑系统所完成的逻辑功能

  • Verilog语法自由,易学易用,适用于算法级,门级设计,代码简介,发展较快

  • Verilog从C语言发展而来的

  • 0 -- 逻辑低电平,条件为假

  • 1 -- 逻辑高电平,条件为真

  • z -- 高阻态,无驱动

  • x -- 未知逻辑电平

  • 实际电路中存在0,1,z,亚稳态

    module example(
    input wire sys_rst_n,//输入信号
    inout wire sda,//输入输出信号,基本不用

    output wire po_flag // 输出信号
    );
    // 只有输入信号是不能直接得到输出信号的
    // 需要通过一些数据和参数对输入信号进行处理,得到输出信号
    // 一般需要对输入信号进行处理之后得到输出信号

    // 线网型变量 -- wire可以被看作是物理连接 -- 被综合成物理连线
    wire [0:0] flag;

    // 寄存器型变量 -- 具有对某一时间点的状态进行保持的功能
    // 可综合电路中被综合成寄存器
    reg [7:0] cnt;

    // 参数的定义
    // parameter 可以在顶层模块通过实例化对参数进行修改
    parameter CNT_MAX = 100;

    //localparam -- 只用在模块内部,不能在实例化的时候进行修改
    localparam CNT_MAX = 100;

    // 模块的实例化
    example
    #(
    .CNT_MAX (8'd100) // 实例化时参数可以修改
    )
    example_inst
    (
    .sys_clk (sys_clk),
    .sys_rst_n (sys_rst_n),
    .po_flag (po_flag)
    );

    /*
    常量
    表示方法: [换算为二进制数之后的位宽]'[进制符号][与数值对应的进制数]
    8'd171
    d -- 十进制
    b -- 二进制
    h -- 十六进制

    不写位宽 -- verilog会自动进行匹配
    总位宽大于实际位宽,自动补0,总位宽小于实际位宽,则自动截断左边超出的位数
    d'7  8'd7   -- 8'b0000_0111   前面补5个0
    2'd7 换算位二进制是 2'b11 (7换成二进制是111),总长度位2位,所以截掉左侧的部分
    直接写参数 100 ,表示位宽为32bit的十进制数100

    */

    // 赋值方式
    // 阻塞赋值 =
    // 阻塞赋值可以看作是顺序执行,每条语句执行完成之后才能执行下一条语句
    a = 1;
    b = 2;
    c = 3;
    begin
    a = b;
    c = a;
    end
    a = 2;
    b = 2;
    c = 2;
    // 非阻塞赋值 <= 并行执行
    a = 1;
    b = 2;
    c = 3;
    begin
    a <= b; // 同一时刻,这两条语句是同时执行的
    c <= a;
    end
    a = 2;
    b = 2;
    c = 1;

    // always语句
    always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
    cnt <= 8'b0; // 进行寄存器复位
    else if (cnt == CNT_MAX) // 寄存器中的值记到最大的时候,维持最大值
    cnt <= CNT_MAX;
    else
    cnt <= cnt + 8'd1; // 每个周期都加1

    // assign语句
    assign po_flag = (cnt == CNT_MAX) ? 1'b1 : 1'b0;
    endmoudle

    +

    • -- 一般不用
      / -- 一般不用
      % -- 求余数,两边都为整型数据
  • & -- 既可以作为单目运算符又可以作为双目运算符

    // & 作为单目运算符,&m,将m的所有bit相与,得到结果是1bit
    4'b1111 = 1&1&1&1 = 1'b1
    4'b1101 = 1&1&0&1 = 1'b0

    // & 作为双目运算符表示按位与,m&n,是将m的每个bit与n的每个bit相与,在运算的时候保证m和n的bit数相等,最后结果位数和m(n)的bit数相等
    4'b1010&4'b0101 = 4'b0000

  • &,^,^,|,~| -- 这些运算符都有相同的看法

  • &&/||/! -- 与或非三种逻辑运算,返回值为0或者是1

  • 逻辑运算符两边非0位真,0为假

    a = 4'ha
    b = 4'd0
    c = a && b // c的值为0

    a < b a > b
    a <= b a >= b

  • 移位运算符是二元运算符,<<和>>,将运算符左边的数左移或者是右移指定的位数,用0来补充空闲位

    b <= a << 1; // a的每一位都左移一位,结果赋值给b b <= a >> 1; // a的没一位都右移一位,结果赋值给b

  • 在应用移位运算符的时候一定要注意他的特性:就是空闲位使用0来补充,也就是说,一个二进制数不管原来的数值是多少,只要一直移位,最终都会变为0

    4'b1000 >> 3; // 4'b0001
    4'b1000 >> 4; // 4'b0000

  • 移位运算符可以代替乘法和除法,左移一位可以看成是乘2,右移一位可以看成是除2,但是要注意位拓展

  • {,} -- 位拼接运算符

    a = 8'b1011_1111;
    b = 3'b011;
    c = 5'b11011;
    {a,b,c} ; // 拼接成16bit

    [表达式1]?表达式2:表达式3
    a = 6;
    b = 7;
    c = (a>b)?a:b;

  • 一元运算符>二元运算符>三元运算符

  • != > 按位运算 > && > || > 条件运算

  • 归约运算符 > 算术运算符 > 移位运算符 > 关系运算符 > == 和 !=

  • 使用()提升优先级

    if(表达式) // 这种写法在always语句块产生组合逻辑的时候中会产生latch,不推荐使用
    语句块;

    if(表达式)
    语句块1;
    else if(表达式2)
    语句块2;
    ….
    else
    语句; // 这种是最常用的写法

    switch(表达式)
    case value1 : 语句1;
    case value2 : 语句2;
    ….
    default: 语句;

    `timescale 1ns/1ns // 时间尺度预编译指令 时间单位/时间精度
    时间单位和时间精度有由1,10及100以及s,ms,ns,us,ps,fs组成
    时间单位:定义仿真过程中所有与时间相关量的单位
    仿真中使用#数字表示延迟相应的时间单位的时间,例如#10表示延时10个时间单位,即10ns

    时间精度:决定时间相关量的精度及仿真显示的最小单位
    `timescale 1ns/10ps // 精度0.01, #10.11 表示延迟10110ps

    `timescale 100ps/1ns // 错误,时间单位不能比时间精度小

    // 主要的函数,在支持verilog语法的编译器中都会显示为高亮的关键字
    $display //打印信息,自动换行
    $write //打印信息
    $strobe //打印信息,自动换行,最后执行
    $monitor //检测变量
    $stop //暂停仿真
    $finish //结束仿真
    $time //时间函数
    $random //随机函数
    $readmemb //读取文件的函数

$display()

$display(); //  输出,打印信息
$display("%b+%b = %d",a,b,c);
// %d %D -- 十进制
// %o %O -- 八进制
// %b %B -- 二进制
// %d %D -- 十六进制
// 不指定进制,默认为十进制数


`timescale 1ns/1ns

module tb_test();
  reg [3:0] a;
  reg [3:0] b;
  reg [3:0] c;

  // initial只在仿真中使用,是不可综合的
  // 仿真开始,只执行一次
  initial begin
    $display("Hello");
    $display("EmbedFire");
    a = 4'd5;
    b = 4'd6;
    c = a + b;
    #100;
    $display("%b + %b = %d",a,b,c);

  end
endmodule

result:
Hello
EmbedFire
010 + 0110 = 11

$write()

  • write() -- 输出打印信息 -- 函数不会自动进行换行

    `timescale 1ns/1ns

    module tb_test();
    reg [3:0] a;
    reg [3:0] b;
    reg [3:0] c;

    // initial只在仿真中使用,是不可综合的
    // 仿真开始,只执行一次
    initial begin
    $write("Hello");
    $write("EmbedFire");
    a = 4'd5;
    b = 4'd6;
    c = a + b;
    #100;
    $write("%b + %b = %d",a,b,c);

    end
    endmodule

$strobe()

  • 输出打印函数 -- 在最后进行执行

    `timescale 1ns/1ns

    module tb_test();
    reg [3:0] a;
    reg [3:0] b;
    reg [3:0] c;

    // initial只在仿真中使用,是不可综合的
    // 仿真开始,只执行一次
    initial begin
    $strobe("strobe:Hello"); // 写在最前面,但是执行在最后
    a = 4'd5;
    $display("EmbedFire");
    $display("display:%b + %b = %d",a,b,c); // 在执行这条语句的时候b,c都没有进行赋值
    // 所以b,c未知的x
    b = 4'd6;
    c = a + b;
    #100;

    end
    endmodule

$monitor()

  • 检测变量,只要打印的变量发生了变化,就会执行一次

    `timescale 1ns/1ns

    module tb_test();

    reg [3:0] a;
    reg [3:0] b;
    reg [3:0] c;
    
    initial begin
        a = 4'd5;
        #100;
        b = 4'd6;
        #100;
        c = a + b;
    end
    
    initial begin
        $monitor("%b + %b = %d",a,b,c);
    end

    endmodule

$stop

  • 暂停执行仿真

    `timescale 1ns/1ns

    module tb_test();
    initial begin
    $display("hello");
    $display("EmbedFire");
    #100;
    $display("Stop Simulation");
    $stop;
    #100;
    $display("Finish Simulation");
    $finish; // 结束仿真
    end
    endmodule

$time

`timescale 1ns/1ns

module tb_test();
  reg [3:0] a;
  always #10 a = $random;
  initial $monitor("a = %d @time %d",a,$time);
endmodule

$readmemb 和 $readmemh

  • $readmemb -- 用于读取二进制文件函数

  • $readmemh -- 用于读取十六进制文件函数

    $readmemb("数据文件名",存储器名);
    $readmemh("数据文件名",存储器名);

    `timescale 1ns/1ns

    module tb_test();

    integer i;
    reg [7:0] a [20:0]; // a 为宽度为8bit,深度为21bit的存储器

    initial begin
    $readmemb("EmbedFire.txt",a);
    for(i = 0;i<=20;i=i+1) begin
    #10;
    $write("%s",a[i]); // Welcome to EmbedFire!
    end
    end
    endmodule

读取的文件

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章