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'b0, th0, tl0}把1位的0、8位的定时器高字节th0、8位的定时器低字节tl0拼接成一个17位的临时数值。对这个数值执行+1'b1,就是模拟16位计数器的递增操作——实际计数器是th0+tl0组成的16位,前面加1位0是为了捕获溢出时的进位。左侧拼接赋值
{tf0, th0, tl0}把加1后的17位结果按位拆分:- 最高位(第17位)赋值给
tf0:当16位计数器从0xFFFF加1时,会产生溢出进位,这个进位就会被赋值给tf0(定时器0的溢出标志位),标志定时器完成一次计数周期。 - 中间8位赋值给
th0,最后8位赋值给tl0:完成计数器高、低字节的更新,溢出后自然会变成0x0000。
- 最高位(第17位)赋值给
非阻塞赋值的作用
用<=而非=是因为这是时钟沿触发的时序逻辑,非阻塞赋值保证所有赋值操作都是基于上一个时钟周期的旧值计算,在当前时钟沿结束后同时更新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
相关产品推荐
相关产品推荐

