首页
搜索 搜索
当前位置:商业资讯 > 正文

观天下!02-初识Verilog

2023-05-20 22:26:25 博客园


【资料图】

1.开发环境搭建

需要使用的软件:

QuartusIIModelSimVisioNotepad++2.初识Verilog2.1 Verilog HDL简介Verilog HDL是一种硬件描述语言,以文本形式来描述数字系统硬件的结构和行为的语言,用它可以表示逻辑电路图\逻辑表达式,还可以表示数字逻辑系统所完成的逻辑功能Verilog语法自由,易学易用,适用于算法级,门级设计,代码简介,发展较快Verilog从C语言发展而来的2.2 逻辑值0 -- 逻辑低电平,条件为假1 -- 逻辑高电平,条件为真z -- 高阻态,无驱动x -- 未知逻辑电平实际电路中存在0,1,z,亚稳态2.3 关键字
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
2.4 算术运算符
+-* -- 一般不用/ -- 一般不用% -- 求余数,两边都为整型数据
2.5 归约运算符\按位运算符& -- 既可以作为单目运算符又可以作为双目运算符
// & 作为单目运算符,&m,将m的所有bit相与,得到结果是1bit4"b1111 = 1&1&1&1 = 1"b14"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
&,^,^,|,~| -- 这些运算符都有相同的看法2.6 逻辑运算符&&/||/! -- 与或非三种逻辑运算,返回值为0或者是1逻辑运算符两边非0位真,0为假
a = 4"hab = 4"d0c = a && b   // c的值为0
2.7 关系运算符
a < ba > ba <= ba >= b
2.8 移位运算符移位运算符是二元运算符,<<和>>,将运算符左边的数左移或者是右移指定的位数,用0来补充空闲位
b <= a << 1; // a的每一位都左移一位,结果赋值给bb <= a >> 1; // a的没一位都右移一位,结果赋值给b
在应用移位运算符的时候一定要注意他的特性:就是空闲位使用0来补充,也就是说,一个二进制数不管原来的数值是多少,只要一直移位,最终都会变为0
4"b1000 >> 3;  // 4"b00014"b1000 >> 4;  // 4"b0000
移位运算符可以代替乘法和除法,左移一位可以看成是乘2,右移一位可以看成是除2,但是要注意位拓展2.9 拼接运算符{,} -- 位拼接运算符
a = 8"b1011_1111;b = 3"b011;c = 5"b11011;{a,b,c} ; // 拼接成16bit
2.10 三元运算符
[表达式1]?表达式2:表达式3a = 6;b = 7;c = (a>b)?a:b;
2.11 优先级一元运算符>二元运算符>三元运算符!= > 按位运算 > && > || > 条件运算归约运算符 > 算术运算符 > 移位运算符 > 关系运算符 > == 和 !=使用()提升优先级2.12 if-else语句
if(表达式)   // 这种写法在always语句块产生组合逻辑的时候中会产生latch,不推荐使用  语句块;if(表达式)  语句块1;else if(表达式2)  语句块2;....else  语句;    // 这种是最常用的写法
2.12 case语句
switch(表达式)  case value1 : 语句1;  case value2 : 语句2;  ....  default: 语句;
2.13 系统函数
`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/1nsmodule 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);      endendmoduleresult:HelloEmbedFire010 + 0110 = 11
$write()write() -- 输出打印信息 -- 函数不会自动进行换行
`timescale 1ns/1nsmodule 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);      endendmodule
$strobe()输出打印函数 -- 在最后进行执行
`timescale 1ns/1nsmodule 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;    endendmodule
$monitor()检测变量,只要打印的变量发生了变化,就会执行一次
`timescale 1ns/1nsmodule tb_test();reg [3:0] a;reg [3:0] b;reg [3:0] c;initial begina = 4"d5;#100;b = 4"d6;#100;c = a + b;endinitial begin$monitor("%b + %b = %d",a,b,c);endendmodule
$stop暂停执行仿真
`timescale 1ns/1nsmodule tb_test();  initial begin    $display("hello");    $display("EmbedFire");    #100;    $display("Stop Simulation");    $stop;    #100;    $display("Finish Simulation");    $finish; // 结束仿真  endendmodule
$time
`timescale 1ns/1nsmodule 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/1nsmodule 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  endendmodule

读取的文件