为什么有些寄存器值的更新会延迟一个时钟周期变化

Posted 园游会丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么有些寄存器值的更新会延迟一个时钟周期变化相关的知识,希望对你有一定的参考价值。

module counter(
        input              clk,
        input              rst_n,
        output  reg[7:0]   data_out
 
);


//reg  define
reg  [3:0] cnt;            //假设计数器每计数十次,溢满一次
reg  [2:0] lsm_cnt;   //计数器每溢满一次,lsm_cnt寄存器自加1

//parameter [7:0] num = 8\'b10110101;此句和下面两句功能等效
wire [7:0] num;       //定义一个八位数据
assign num = 8\'b10110101;   

initial  data_out <= 8\'b0000_0000; //如果此处没有给初值,那么在仿真中不知道每一位是0还是1,data_out的值将呈现高阻态为8‘dX,
                                              //只有后面case语句执行8次将寄存器的每一位都写入值后,仿真中data_out才有值显示出来
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt <= 4\'d0;
    else if(cnt < 9)           //如果是发送数据则需要一个标志位去启动计数器开始计数
        cnt <= cnt + 1\'b1;
   else
        cnt <= 4\'d0;
end


always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        lsm_cnt <= 3\'d0;
    else if(cnt == 9)
        lsm_cnt <= lsm_cnt + 1\'b1;
   else 
        lsm_cnt <= lsm_cnt;
end


always@(posedge clk )begin
      if(cnt == 1)           //注意此处的作用是为了防止系统在空闲状态下还继续进行发送数据
          case(lsm_cnt)
              0: data_out[0] <= num[0]; 
              1: data_out[1] <= num[1]; 
              2: data_out[2] <= num[2]; 
              3: data_out[3] <= num[3];        
              4: data_out[4] <= num[4]; 
              5: data_out[5] <= num[5]; 
              6: data_out[6] <= num[6]; 
              7: data_out[7] <= num[7]; 
             default : data_out <= 8\'d0;
          endcase
//     else
//        data_out <= data_out;
end


endmodule

 

 仿真代码:

`timescale 1ns/1ps
module counter_tb;

reg sys_clk;
reg sys_rst_n;
wire [7:0] data_out;   

//例化
 counter u_counter(
         .clk(sys_clk),
         .rst_n(sys_rst_n),
           .data_out(data_out)
);
 
initial begin
    sys_clk = 1\'b1;    
   sys_rst_n = 1\'b0;
   #100;    
   sys_rst_n = 1\'b1;
   #900 $stop;
end     

always #10  sys_clk = ~sys_clk;

endmodule

 

 

 

 

非阻塞(non-blocking)赋值方式 ( b<= a): b的值被赋成新值a的操作, 并不是立刻完成的,而是在单个块结束时才完成; 块内的多条赋值语句在单个块结束时同时赋值; 硬件有对应的电路。

阻塞(blocking)赋值方式 ( b = a): b的值立刻被赋成新值a; 完成该赋值语句后才能执行下一句的操作; 硬件没有对应的电路,因而综合结果未知。

       这里还要特别注意是‘“块内的多条赋值语句在块结束时同时赋值”不能考虑成这个时钟周期末尾到下个时钟上升沿完成赋值,因为always块语句的执行时间很短,要不了一个时钟周期的时间,即真是的赋值是在当前的时钟上升沿延迟一点点时间就完成了新的赋值操作即时序仿真。结合程序这就解释了,lsm_cnt和data_out寄存器中的值更新为什么延迟了cnt寄存器一个时钟周期;而cnt寄存器的并未延时一个时钟周期才改变(实际上cnt的值在上升沿到来后,延迟了一点时间就更新了,所以门级仿真就等效在当前上升沿变化了)。

       在不同always块里对同一个寄存器进行赋值是不被允许的,会发生数据竞争。

       如果在一个always块中调用另一个always块中的寄存器的某个值作为该always块的判断条件时,则该always块的仿真时序图会延迟一个时钟周期才发生变化。代码如下:

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        lsm_cnt <= 3\'d0;
    else if(cnt == 9)
        lsm_cnt <= lsm_cnt + 1\'b1;
   else 
        lsm_cnt <= lsm_cnt;
end

 

       lsm_寄存器既可以用于数据发送时,发送到第几位的依据标志;也可以用于序列机产生时钟分频时,分频时钟的高低电平变化的依据标志。

 

以上是关于为什么有些寄存器值的更新会延迟一个时钟周期变化的主要内容,如果未能解决你的问题,请参考以下文章

时序逻辑电路基础

Verilog中想要让某个项延迟几个时钟周期,最好的办法是啥

计算机原理 6.15 多周期MIPS CPU数据通路

Verilog如何检测一个时钟周期肿的翻转?

Modelsim仿真一些简单问题

数字IC设计——跨时钟域篇4(多比特处理)