You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Verilog非阻塞赋值两侧使用拼接操作的工作原理问询

8051定时器Verilog代码中拼接+非阻塞赋值语句的解析

我是Verilog/HDL初学者,在项目里看到一段Intel 8051微控制器定时器模块的代码,其中有这么一条非阻塞赋值语句,两侧都用了拼接操作,完全搞不懂它的工作机制:

{tf0, th0,tl0} <= {1'b0, th0, tl0}+ 1'b1;

相关完整代码如下:

module oc8051_tc (clk, rst, 
            data_in,
            wr_addr,
        wr, wr_bit,
        ie0, ie1,
        tr0, tr1,
        t0, t1,
            tf0, tf1,
        pres_ow,
//registers
        tmod, tl0, th0, tl1, th1);

input [7:0]  wr_addr,
             data_in;
input        clk,
             rst,
         wr,
         wr_bit,
         ie0,
         ie1,
         tr0,
         tr1,
         t0,
         t1,
         pres_ow;
output [7:0] tmod,
             tl0,
         th0,
         tl1,
         th1;
output       tf0,
             tf1;


reg [7:0] tmod, tl0, th0, tl1, th1;
reg tf0, tf1_0, tf1_1, t0_buff, t1_buff;

wire tc0_add, tc1_add;

assign tc0_add = (tr0 & (!tmod[3] | !ie0) & ((!tmod[2] & pres_ow) | (tmod[2] & !t0 & t0_buff)));
assign tc1_add = (tr1 & (!tmod[7] | !ie1) & ((!tmod[6] & pres_ow) | (tmod[6] & !t1 & t1_buff)));
assign tf1= tf1_0 | tf1_1;

//
// read or write from one of the addresses in tmod
//
always @(posedge clk or posedge rst)
begin
 if (rst) begin
   tmod <=#1 `OC8051_RST_TMOD;
 end else if ((wr) & !(wr_bit) & (wr_addr==`OC8051_SFR_TMOD))
    tmod <= #1 data_in;
end

//
// TIMER COUNTER 0
//
always @(posedge clk or posedge rst)
begin
 if (rst) begin
   tl0 <=#1 `OC8051_RST_TL0;
   th0 <=#1 `OC8051_RST_TH0;
   tf0 <= #1 1'b0;
   tf1_0 <= #1 1'b0;
 end else if ((wr) & !(wr_bit) & (wr_addr==`OC8051_SFR_TL0)) begin
   tl0 <= #1 data_in;
   tf0 <= #1 1'b0;
   tf1_0 <= #1 1'b0;
 end else if ((wr) & !(wr_bit) & (wr_addr==`OC8051_SFR_TH0)) begin
   th0 <= #1 data_in;
   tf0 <= #1 1'b0;
   tf1_0 <= #1 1'b0;
 end else begin
     case (tmod[1:0]) /* synopsys full_case parallel_case */
      `OC8051_MODE0: begin                       // mode 0
        tf1_0 <= #1 1'b0;
        if (tc0_add)
          {tf0, th0,tl0[4:0]} <= #1 {1'b0, th0, tl0[4:0]}+ 1'b1;
      end
      `OC8051_MODE1: begin                       // mode 1
        tf1_0 <= #1 1'b0;
        if (tc0_add)
          {tf0, th0,tl0} <= #1 {1'b0, th0, tl0}+ 1'b1;
      end

      `OC8051_MODE2: begin                       // mode 2
        tf1_0 <= #1 1'b0;
        if (tc0_add) begin
      if (tl0 == 8'b1111_1111) begin
            tf0 <=#1 1'b1;
            tl0 <=#1 th0;
           end
          else begin
            tl0 <=#1 tl0 + 8'h1;
            tf0 <= #1 1'b0;
          end
    end
      end
      `OC8051_MODE3: begin                       // mode 3

     if (tc0_add)
       {tf0, tl0} <= #1 {1'b0, tl0} +1'b1;

         if (tr1 & pres_ow)
       {tf1_0, th0} <= #1 {1'b0, th0} +1'b1;

      end
/*      default:begin
        tf0 <= #1 1'b0;
        tf1_0 <= #1 1'b0;
      end*/
    endcase
 end
end

语句工作机制解析

这条语句是8051定时器模式1(16位定时器/计数器)下的核心计数逻辑,拆解来看:

  1. 右侧拼接与加1操作
    {1'b0, th0, tl0} 把1位的0、8位的定时器高字节th0、8位的定时器低字节tl0拼接成一个17位的临时数值。对这个数值执行+1'b1,就是模拟16位计数器的递增操作——实际计数器是th0+tl0组成的16位,前面加1位0是为了捕获溢出时的进位。

  2. 左侧拼接赋值
    {tf0, th0, tl0} 把加1后的17位结果按位拆分:

    • 最高位(第17位)赋值给tf0:当16位计数器从0xFFFF加1时,会产生溢出进位,这个进位就会被赋值给tf0(定时器0的溢出标志位),标志定时器完成一次计数周期。
    • 中间8位赋值给th0,最后8位赋值给tl0:完成计数器高、低字节的更新,溢出后自然会变成0x0000。
  3. 非阻塞赋值的作用
    用<=而非=是因为这是时钟沿触发的时序逻辑,非阻塞赋值保证所有赋值操作都是基于上一个时钟周期的旧值计算,在当前时钟沿结束后同时更新tf0、th0、tl0,避免了分开赋值可能出现的时序竞争或逻辑错误。

延伸:其他模式的类似写法

代码里的模式0(13位计数器)用了{tf0, th0,tl0[4:0]} <= {1'b0, th0, tl0[4:0]}+ 1'b1;,原理完全一致:只是把计数器换成了th0(8位)+tl0低5位组成的13位,加1后捕获进位到tf0,同时更新计数器值。这种拼接写法能统一不同定时器模式的计数逻辑,简洁且不易出错。


内容的提问来源于stack exchange,提问作者Gundi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 17:50:34