前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >verilog_移位寄存器_仿真(程序逐句解释)

verilog_移位寄存器_仿真(程序逐句解释)

作者头像
全栈程序员站长
发布2022-09-13 10:53:49
8490
发布2022-09-13 10:53:49
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

前言

  之前老是想着学的快点,就直接编译了程序就下载在开发板上跑,后来发现这样不行,因为如果程序有问题,验证和纠错的时间成本太高了(毕竟vivado跑一次花的时间很长),反过来学习仿真,下面是一点心得和体会。

开发环境

编译软件及版本:vivado 2019.2 编译语言:verilog

  网上随便找了一个简单程序和仿真,先实现复现,再谈其他。下面我将先给出代码和仿真截图,再说具体的东西。

移位寄存器程序代码:

代码语言:javascript
复制
`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2020/10/16 19:42:58
// Design Name: 
// Module Name: shift_register
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

module shift_register(
    input clock,
    input reset,
    input load,
    input [1:0] sel,
    input [4:0] data,
    output [4:0] shiftreg
    );
    
    reg [4:0] shiftreg;
    always @(posedge clock)
        begin
            if(reset)
                shiftreg = 0;
           else if(load)
                shiftreg = data;
           else
                case(sel)
                    2'b00 : shiftreg = shiftreg;
                    2'b01 : shiftreg = shiftreg << 1;
                    2'b10 : shiftreg = shiftreg >> 1;
                    default : shiftreg = shiftreg;
                endcase
        end

endmodule

移位寄存器的testbench代码:

代码语言:javascript
复制
`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2020/10/17 11:00:37
// Design Name: 
// Module Name: shift_register_tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

module shift_register_tb( 

    );                                                  //declare testbench name
      
    reg clock;
    reg load;
    reg reset;    // declaration of signals
    wire [4:0] shiftreg;
    reg [4:0] data;
    reg [1:0] sel;
 
   // instantiation of the shift_reg design below
    shift_register dut(
        .clock (clock),
        .load (load),
        .reset (reset),
        .shiftreg (shiftreg),
        .data (data),
        .sel (sel)
        );
        
   //this process block sets up the free running clock
    initial 
        begin
            clock = 0;
            forever #50 clock = ~clock;
        end
        
    // this process block specifies the stimulus. 
    initial 
        begin
            reset = 1;
            data = 5'b00000;
            load = 0;
            sel = 2'b00;
            
            #200
                reset = 0;
                load = 1;
            
            #200
                data = 5'b00001;
            
            #100
                sel = 2'b01;
                load = 0;
            
            #200
                sel = 2'b10;
            
            #1000 
                $stop;
        end
 
    initial // this process block pipes the ASCII results to the terminal or text editor
        begin 
            $timeformat(-9,1,"ns",12);
            $display(" Time Clk Rst Ld SftRg Data Sel");
            $monitor("%t %b %b %b %b %b %b", $realtime,clock, reset, load, shiftreg, data, sel);
        end
        
endmodule

仿真截图:

在这里插入图片描述
在这里插入图片描述

正文

    先说说为什么要写这个博客,首先是vivado更新了,用的vivado 2019.2,其次是有些地方感觉别的博主写的不详细,还有就是自己的积累,怕以后忘了。我就详详细细地写一次,一句一句地写,让更多初学者不惧怕或者说不讨厌去读代码。     我自己看别人的代码,如果格式不规整,我就看的很难受,可能有点强迫症吧。规整的代码给人清清爽爽的感觉,一目了然。我刚开始学,一句一句地代码都自己去查意思,觉得很花时间,下面我就系统一点地标注每一句代码,节约大家学习时间和成本。     我自己最开始很不习惯,编硬件描述语言需要编两份,除了主文件,还要写一个testbench???这是什么鬼???其实可以这么理解,因为verilog是硬件描述语言,所以我们编的module这个模块,相当于是实现某种功能,但我们进行仿真的时候,是需要给这个模块信号的,也就是常说的激励。testbench相当于编的是激励,是信号,然后加载在module这个模块上,最后才能验证我们编的module对不对。verilog是用来描述硬件的。     这里理解了以后,下面我开始讲代码的含义。

移位寄存器程序代码逐句讲解:

代码语言:javascript
复制
`timescale 1ns / 1ps

timescale表示模块的时间精度; 1ns就是下面程序模块的仿真时间单位是1ns,1ps的意思是仿真时间精度是1ps。

代码语言:javascript
复制
module shift_register(
    input clock,
    input reset,
    input load,
    input [1:0] sel,
    input [4:0] data,
    output [4:0] shiftreg
    );

这里表示建立了一个移位寄存器模块; 它的输入有clock、reset、load、sel([1:0]代表sel这个变量是两位的(0~1))、 data([4:0]表示data这个变量是5位的(0~4)), 输出有shiftreg([4:0]表示shiftreg这个变量是5位的(0~4))。

代码语言:javascript
复制
reg [4:0] shiftreg;

reg是寄存器,reg [4:0]shiftreg表示了一组寄存器。

代码语言:javascript
复制
always @(posedge clock)
        begin
            if(reset)
                shiftreg = 0;
           else if(load)
                shiftreg = data;
           else
                case(sel)
                    2'b00 : shiftreg = shiftreg;
                    2'b01 : shiftreg = shiftreg << 1;
                    2'b10 : shiftreg = shiftreg >> 1;
                    default : shiftreg = shiftreg;
                endcase
        end

always是一个过程块,当敏感信号表达式的值改变的时候,就执行一遍块内语句; 注意:always过程块是不能嵌套使用的;

代码语言:javascript
复制
always @(<敏感信号表达式>)
	begin
		块内语句
	end

begin和end相当于c/c++里面的{}; posedge clock是指上升沿触发,相当于每次上升沿时,always过程块执行一次; 关键字posedge和negedge分别是上升沿和下降沿; eg.同步时序电路的时钟信号为clk,clear为异步清零信号; 敏感信号可写为:

代码语言:javascript
复制
always @(posedge clk or posedge clear)

上升沿触发,高电平清0有效;

代码语言:javascript
复制
always @(posedge clk or negedge clear)

上升沿触发,低电平清0有效;

代码语言:javascript
复制
		   if(...)
                ...
           else if(...)
                ...
           else
                ...

if(条件1) 如果条件1为真,执行这里;(条件为真才执行) else if(条件2) 否则,当条件2为真执行这里;(当条件1不为真,条件2为真时执行这里) else 否则,执行这里。(条件1,条件2都不为真,执行这里)

代码语言:javascript
复制
  			if(reset)
                shiftreg = 0;

如果reset(复位信号)为1时,将寄存器全部置0;

代码语言:javascript
复制
 			else if(load)
                shiftreg = data;

如果load(加载信号)为1时,将data(数据)的值赋给shiftreg(寄存器);

代码语言:javascript
复制
 			else
                case(sel)
                    2'b00 : shiftreg = shiftreg;
                    2'b01 : shiftreg = shiftreg << 1;
                    2'b10 : shiftreg = shiftreg >> 1;
                    default : shiftreg = shiftreg;
                endcase

否则,当sel(选择)的值为0时,寄存器值不变; 当sel(选择)的值为1时,寄存器值左移一位; 当sel(选择)的值为2时,寄存器值右移一位; default(默认值)是寄存器值不变;

代码语言:javascript
复制
	2'b10

<位宽><进制><数字> 2表示这个数的位宽是2(位宽指的是时间所占位数);这里初学者就简单地理解成位数就行; b表示二进制; 10表示十进制的2;

移位寄存器testbench程序代码逐句讲解:

代码语言:javascript
复制
module shift_register_tb( 

    );                                                  //declare testbench name
      
    reg clock;
    reg load;
    reg reset;    // declaration of signals
    wire [4:0] shiftreg;
    reg [4:0] data;
    reg [1:0] sel;

这里定义了一个叫shift_register_tb的模块; 在testbench(测试程序)中, 逻辑设计中的输入信号在这里对应reg型变量; 逻辑设计中的输出信号在这里对应wire型变量

代码语言:javascript
复制
module shift_register(
    input clock,
    input reset,
    input load,
    input [1:0] sel,
    input [4:0] data,
    output [4:0] shiftreg
    );

这里我们可以对比之前的输入输出定义; input的信号(或者说变量)都在testbench中变成了reg型; output的信号(或者说变量)都在testbench中变成了wire型;

代码语言:javascript
复制
	shift_register dut(
        .clock (clock),
        .load (load),
        .reset (reset),
        .shiftreg (shiftreg),
        .data (data),
        .sel (sel)
        );

dut(device under test) 这个部分说白了就是把咱们之后要加的激励端口,和咱们之前写的端口连接在一起; 以.clock (clock)为例: .clock表示原模块(shift_register)的端口,clock表示当前模块shift_register_tb中的端口, .clock (clock)就表示把两个模块的端口衔接起来; 这在verilog中叫做模块例化,初学者给它看成两个端口衔接的程序就行;

代码语言:javascript
复制
 	initial 
        begin
            clock = 0;
            forever #50 clock = ~clock;
        end

testbench中使用initial或者always语句块产生激励,这里使用的是initial; forever循环语句常用于产生周期性的波形,它只能写在initial块中; #表示延迟的意思 #50表示延迟50个单位,这里延迟的单位由一开始的timescale 1ns/1ps控制;timescale 1ns/1ps表示时间单位是1ns,精度是1ps; 这里#50 就表示延迟50ns; ~表示取反 clock = ~clock的意思就是每过50ns,时钟取反一次;

每过50ns
每过50ns

每过50ns,时钟取反一次

代码语言:javascript
复制
 	 initial 
        begin
            reset = 1;
            data = 5'b00000;
            load = 0;
            sel = 2'b00;
            
            #200
                reset = 0;
                load = 1;
            
            #200
                data = 5'b00001;
            
            #100
                sel = 2'b01;
                load = 0;
            
            #200
                sel = 2'b10;
            
            #1000 
                $stop;
        end

这一段连起来的意思就是: 初始时:    reset值为1、data值为00000(二进制)、load值为0、sel值为00(二进制); 过200ns后:    将reset值置为0、load值置为1; 再过200ns(相当于400ns后):    将data的值置为00001(二进制); 再过100ns(相当于500ns后):    将sel的值置为01(二进制)、load置为0; 再过200ns(相当于700ns后):    将sel的值置为10(二进制); 再过1000ns(相当于1700ns后):    激励(程序)停止赋值;

代码语言:javascript
复制
	initial // this process block pipes the ASCII results to the terminal or text editor
        begin 
            $timeformat(-9,1,"ns",12);
            $display(" Time Clk Rst Ld SftRg Data Sel");
            $monitor("%t %b %b %b %b %b %b", $realtime,clock, reset, load, shiftreg, data, sel);
        end
代码语言:javascript
复制
$timeformat(-9,1,"ns",12);

$是一种标识符,timeformat是打印的格式设置,类似于C语言的printf(); -9表示ns、1是打印时间值时,小数点后保留1位、 “ns”是在时间值后面打印的一个后缀字符串、 12表示这所有的字符串长度为12(相当于9+1+2),不足长度的在之前补空格;

代码语言:javascript
复制
$display(" Time Clk Rst Ld SftRg Data Sel");
$monitor("%t %b %b %b %b %b %b", $realtime,clock, reset, load, shiftreg, data, sel);

display是让控制台显示出字符串,monitor表示动态监测,具体如下图:

在这里插入图片描述
在这里插入图片描述

仿真结果

在这里插入图片描述
在这里插入图片描述

初始时:    reset值为1、data值为00000(二进制)、load值为0、sel值为00(二进制); 200ns时:    reset值为0、data值为00000(二进制)、load值为1、sel值为00(二进制); 400ns时:    reset值为0、data值为00001(二进制)、load值为1、sel值为00(二进制); 500ns时:    reset值为0、data值为00001(二进制)、load值为0、sel值为01(二进制); 550ns时在上升沿:    由于sel值为01,功能是寄存器值左移一位,    所以data的值由00001变成了00010(1左移1位); 650ns时在上升沿:    由于sel值为01,功能是寄存器值左移一位,    所以data的值由00010变成了00100(1再左移1位); 700ns时:    reset值为0、data值为00001(二进制)、load值为0、sel值为10(二进制); 750ns时在上升沿:    由于sel值为10,功能是寄存器值右移一位,    所以data的值由00100变成了00010(1右移1位); 850ns时在上升沿:    由于sel值为10,功能是寄存器值右移一位,    所以data的值由00010变成了00001(1再右移1位); 950ns时在上升沿:    由于sel值为10,功能是寄存器值右移一位,    所以data的值由00001变成了00000(1再右移1位); 1700ns时,仿真程序停止。

   谢谢阅读~

参考文献

链接:https://blog.csdn.net/leon_zeng0/article/details/78441871 链接:https://blog.csdn.net/liudongdong19/article/details/80993203 链接:https://blog.csdn.net/qq_38791897/article/details/107870029

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/160233.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 开发环境
  • 移位寄存器程序代码:
  • 移位寄存器的testbench代码:
  • 仿真截图:
  • 正文
  • 移位寄存器程序代码逐句讲解:
  • 移位寄存器testbench程序代码逐句讲解:
  • 仿真结果
  • 参考文献
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档