// 时钟上升沿触发的时序逻辑 always @(posedge clk) begin if (reset) begin // 复位时计数器归零,注意指定位宽 q <= 10'd0; end elseif (q == 10'd999) begin // 计数到999时,下一个周期回到0 q <= 10'd0; end elsebegin // 正常情况下计数器加1 q <= q + 10'd1; end end
// 时钟上升沿触发的时序逻辑 always @(posedge clk) begin if (shift_ena) begin // 移位模式:{原低3位, 新数据},实现高位先入 q_temp <= {q_temp[2:0], data}; end elseif (count_ena) begin // 计数模式:递减计数 if (q_temp == 4'd0) begin // 到0时下一个数是15(4位全1) q_temp <= 4'd15; end elsebegin // 正常递减 q_temp <= q_temp - 4'd1; end end // 注意:如果两个使能都为0,保持当前值不变 end
// 第一段:组合逻辑,计算下一个状态 always @(*) begin case (current_state) IDLE: begin // 空闲状态:收到1进入S1,否则保持 next_state = data ? S1 : IDLE; end S1: begin // 已收到1个1:再收到1进入S2,否则回到IDLE next_state = data ? S2 : IDLE; end S2: begin // 已收到11:收到0进入S3,收到1保持在S2 next_state = data ? S2 : S3; end S3: begin // 已收到110:收到1进入OUT(成功!),否则回到IDLE next_state = data ? OUT : IDLE; end OUT: begin // 成功状态:一直保持,直到复位 next_state = OUT; end default: begin // 默认状态,防止综合器报警 next_state = IDLE; end endcase end
// 第二段:时序逻辑,状态更新 always @(posedge clk) begin if (reset) begin // 复位时回到空闲状态 current_state <= IDLE; end elsebegin // 正常情况下更新到下一个状态 current_state <= next_state; end end
// 第一段:组合逻辑,计算下一个状态 always @(*) begin case (current_state) IDLE: begin // 复位后直接进入使能状态 next_state = ENA; end ENA: begin // 计数到3(4个周期)时停止 next_state = (counter == 3'd3) ? STOP : ENA; end STOP: begin // 停止状态一直保持 next_state = STOP; end default: begin next_state = IDLE; end endcase end
// 第二段:时序逻辑,状态更新 always @(posedge clk) begin if (reset) begin current_state <= IDLE; end elsebegin current_state <= next_state; end end
// 第三段:计数器逻辑 always @(posedge clk) begin if (reset) begin counter <= 3'd0; end elsebegin case (next_state) IDLE: begin counter <= 3'd0; end ENA: begin // 使能状态下计数器递增 counter <= counter + 3'd1; end STOP: begin counter <= 3'd0; end default: begin counter <= 3'd0; end endcase end end
// 第一段:组合逻辑,计算下一个状态 always @(*) begin case (current_state) S: begin // 初始状态:等待序列开始 next_state = data ? S1 : S; end S1: begin // 收到1个1 next_state = data ? S11 : S; end S11: begin // 收到11 next_state = data ? S11 : S110; end S110: begin // 收到110 next_state = data ? B0 : S; end B0: begin // 移位使能第1个周期 next_state = B1; end B1: begin // 移位使能第2个周期 next_state = B2; end B2: begin // 移位使能第3个周期 next_state = B3; end B3: begin // 移位使能第4个周期 next_state = Count; end Count: begin // 计数状态:等待done_counting next_state = done_counting ? Wait : Count; end Wait: begin // 等待状态:等待ack确认 next_state = ack ? S : Wait; end default: begin next_state = S; end endcase end
// 第二段:时序逻辑,状态更新 always @(posedge clk) begin if (reset) begin current_state <= S; end elsebegin current_state <= next_state; end end
// 组合逻辑:计算已经完成的千周期数 always @(*) begin if (num <= 16'd1000) begin already_count = 4'd0; end elseif (num > 16'd1000 && num <= 16'd2000) begin already_count = 4'd1; end elseif (num > 16'd2000 && num <= 16'd3000) begin already_count = 4'd2; end elseif (num > 16'd3000 && num <= 16'd4000) begin already_count = 4'd3; end elseif (num > 16'd4000 && num <= 16'd5000) begin already_count = 4'd4; end elseif (num > 16'd5000 && num <= 16'd6000) begin already_count = 4'd5; end elseif (num > 16'd6000 && num <= 16'd7000) begin already_count = 4'd6; end elseif (num > 16'd7000 && num <= 16'd8000) begin already_count = 4'd7; end elseif (num > 16'd8000 && num <= 16'd9000) begin already_count = 4'd8; end elseif (num > 16'd9000 && num <= 16'd10000) begin already_count = 4'd9; end elseif (num > 16'd10000 && num <= 16'd11000) begin already_count = 4'd10; end elseif (num > 16'd11000 && num <= 16'd12000) begin already_count = 4'd11; end elseif (num > 16'd12000 && num <= 16'd13000) begin already_count = 4'd12; end elseif (num > 16'd13000 && num <= 16'd14000) begin already_count = 4'd13; end elseif (num > 16'd14000 && num <= 16'd15000) begin already_count = 4'd14; end elsebegin already_count = 4'd15; end end
// 时序逻辑:主计数器 always @(posedge clk) begin if (reset) begin num <= 16'd0; end elseif (next_state == Done) begin num <= 16'd0; end elseif (next_state == Count_1000) begin num <= num + 16'd1; end end
case (current_state) IDLE: begin next_state = data ? S1 : IDLE; end S1: begin next_state = data ? S2 : IDLE; end S2: begin next_state = data ? S2 : S3; end S3: begin next_state = data ? C0 : IDLE; end C0: begin next_state = C1; delay[3] = data; // 读取第1位(最高位) end C1: begin next_state = C2; delay[2] = data; // 读取第2位 end C2: begin next_state = C3; delay[1] = data; // 读取第3位 end C3: begin next_state = Count_1000; delay[0] = data; // 读取第4位(最低位) end Count_1000: begin next_state = count_state ? Done : Count_1000; end Done: begin next_state = ack ? IDLE : Done; end default: begin next_state = IDLE; end endcase end
// 第二段:时序逻辑,状态更新 always @(posedge clk) begin if (reset) begin current_state <= IDLE; end elsebegin current_state <= next_state; end end
// 错误示例:移位方向反了 always @(posedge clk) begin if (shift_ena) q_temp <= {data, q_temp[3:1]}; // 低位先入 end
错误原因:
没有理解”高位先入”的含义:新数据应该放在最低位,这样随着移位进行,先进入的数据会逐渐移到高位
拼接运算符的顺序搞反了
正确做法:
1 2 3 4 5
// 正确示例:高位先入 always @(posedge clk) begin if (shift_ena) q_temp <= {q_temp[2:0], data}; // 保留低3位,新数据放最低位 end
调试技巧:
画一个简单的示意图,标注每个时钟周期后寄存器的值
先输入4位已知数据(比如1101),看输出是否符合预期
坑点3:FSM状态遗漏或转移错误
错误表现:
1 2 3 4 5 6 7 8
// 错误示例:状态转移错误 always @(*) begin case (current_state) S11: next_state = data ? S11 : S110; S110: next_state = data ? B0 : IDLE; // 正确 // 漏掉了B0、B1等状态的处理! endcase end
错误原因:
没有仔细对照状态转移图
case语句中漏掉了某些状态
正确做法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// 正确示例:完整的状态转移 always @(*) begin case (current_state) S: next_state = data ? S1 : S; S1: next_state = data ? S11 : S; S11: next_state = data ? S11 : S110; S110: next_state = data ? B0 : S; B0: next_state = B1; B1: next_state = B2; B2: next_state = B3; B3: next_state = Count; Count: next_state = done_counting ? Wait : Count; Wait: next_state = ack ? S : Wait; default: next_state = S; // 加上default endcase end
调试技巧:
先画出完整的状态转移图
使用parameter定义所有状态,避免使用魔术数字
一定要加上default分支,防止综合器报警和出现意外行为
坑点4:组合逻辑中使用阻塞赋值不当
错误表现:
1 2 3 4 5 6 7 8 9 10
// 错误示例:在组合逻辑中对变量赋值产生锁存器 always @(*) begin case (current_state) C0: begin next_state = C1; delay[3] = data; // 在组合逻辑中对delay赋值 end // ... 其他状态没有给delay赋值,会产生锁存器 endcase end
错误原因:
在组合逻辑中对变量赋值,但不是所有分支都赋值,会产生锁存器
delay应该用寄存器存储,在时序逻辑中赋值
正确做法:
1 2 3 4 5 6 7 8 9
// 正确示例:用时序逻辑存储delay always @(posedge clk) begin case (next_state) C0: delay[3] <= data; C1: delay[2] <= data; C2: delay[1] <= data; C3: delay[0] <= data; endcase end