// 状态参数定义:用状态表示连续1的个数 parameter NONE = 4'd0; // 0个连续的1 parameter ONE = 4'd1; // 1个连续的1 parameter TWO = 4'd2; // 2个连续的1 parameter THREE = 4'd3; // 3个连续的1 parameter FOUR = 4'd4; // 4个连续的1 parameter FIVE = 4'd5; // 5个连续的1 parameter SIX = 4'd6; // 6个连续的1 parameter ERROR = 4'd7; // 7个或更多连续的1 parameter DISC = 4'd8; // 丢弃状态 parameter FLAG = 4'd9; // 帧标志状态
reg [3:0] current_state; reg [3:0] next_state;
// 第一段:组合逻辑,状态转移 always @(*) begin case (current_state) NONE: begin next_state = in ? ONE : NONE; end ONE: begin next_state = in ? TWO : NONE; end TWO: begin next_state = in ? THREE : NONE; end THREE: begin next_state = in ? FOUR : NONE; end FOUR: begin next_state = in ? FIVE : NONE; end FIVE: begin next_state = in ? SIX : DISC; // 5个1后:1→去SIX,0→丢弃 end SIX: begin next_state = in ? ERROR : FLAG; // 6个1后:1→错误,0→帧标志 end DISC: begin next_state = in ? ONE : NONE; // 丢弃后重新开始计数 end FLAG: begin next_state = in ? ONE : NONE; // 帧标志后重新开始计数 end ERROR: begin next_state = in ? ERROR : NONE; // 错误状态:遇到0才退出 end default: begin next_state = NONE; end endcase end
// 第二段:时序逻辑,状态更新 always @(posedge clk) begin if (reset) begin current_state <= NONE; endelsebegin current_state <= next_state; end end
// 第三段:输出逻辑 always @(posedge clk) begin if (reset) begin disc <= 1'd0; flag <= 1'd0; err <= 1'd0; endelsebegin case (next_state) // 注意:用next_state! DISC: begin disc <= 1'd1; flag <= 1'd0; err <= 1'd0; end FLAG: begin disc <= 1'd0; flag <= 1'd1; err <= 1'd0; end ERROR: begin disc <= 1'd0; flag <= 1'd0; err <= 1'd1; end default: begin disc <= 1'd0; flag <= 1'd0; err <= 1'd0; end endcase end end
// 第一段:组合逻辑,状态转移 always @(*) begin case (current_state) NONE: begin next_state = in ? DATA : NONE; end DATA: begin case (counter) 3'd5: next_state = in ? DATA : DISC; // 5个1 3'd6: next_state = in ? ERROR : FLAG; // 6个1 default:next_state = in ? DATA : NONE; // 其他情况 endcase end DISC: begin next_state = in ? DATA : NONE; end FLAG: begin next_state = in ? DATA : NONE; end ERROR: begin next_state = in ? ERROR : NONE; end default: begin next_state = NONE; end endcase end
// 第二段:时序逻辑,状态更新 always @(posedge clk) begin if (reset) begin current_state <= NONE; endelsebegin current_state <= next_state; end end
// 第三段:计数器和输出逻辑 always @(posedge clk) begin if (reset) begin disc <= 1'd0; flag <= 1'd0; err <= 1'd0; counter <= 3'd0; endelsebegin case (next_state) DATA: begin disc <= 1'd0; flag <= 1'd0; err <= 1'd0; counter <= counter + 1'd1; // 继续计数 end DISC: begin disc <= 1'd1; flag <= 1'd0; err <= 1'd0; counter <= 3'd0; // 计数器清零 end FLAG: begin disc <= 1'd0; flag <= 1'd1; err <= 1'd0; counter <= 3'd0; end ERROR: begin disc <= 1'd0; flag <= 1'd0; err <= 1'd1; counter <= 3'd0; end default: begin disc <= 1'd0; flag <= 1'd0; err <= 1'd0; counter <= 3'd0; end endcase end end
// 第一段:组合逻辑,状态转移 always @(*) begin case (current_state) S0: next_state = x ? S1 : S0; // 看到1去S1,否则留S0 S1: next_state = x ? S1 : S2; // 看到0去S2(找到'10') S2: next_state = x ? S1 : S0; // 关键点! endcase end
// 第二段:时序逻辑,状态更新(异步低电平复位) always @(posedge clk ornegedge aresetn) begin if (~aresetn) begin current_state <= S0; endelsebegin current_state <= next_state; end end
// 第三段:输出逻辑(米利型:看当前状态和输入!) always @(*) begin z = (current_state == S2) ? x : 1'b0; end
// 交叠检测:看到1回到S1,作为下一个序列的开始 S2: next_state = x ? S1 : S0;
错误3:异步复位信号名和电平搞反
错误表现:
1 2 3 4 5 6
// 题目说aresetn是低电平有效,却写成posedge always @(posedge clk orposedge aresetn) begin// 错了! if (aresetn) begin current_state <= S0; end end
错误原因:
信号名后缀_n通常表示低电平有效(negative)
低电平有效复位应该用negedge
正确做法:
1 2 3 4 5 6
// 低电平有效复位的正确写法 always @(posedge clk ornegedge aresetn) begin if (~aresetn) begin current_state <= S0; end end
错误4:HDLC状态机中忘记错误状态的自环
错误表现:
1 2 3
ERROR: begin // 没有写next_state! end
错误原因:
错误状态应该保持在错误状态,直到看到0为止
或者根据题目要求,有些设计是一直保持错误
正确做法:
1 2 3
ERROR: begin next_state = in ? ERROR : NONE; // 看到0才退出错误 end
错误5:输出逻辑用state而不是next_state
错误表现:
1 2 3 4 5 6 7 8 9 10
// 输出用state判断,会晚一个周期 always @(posedge clk) begin if (reset) begin disc <= 1'd0; endelsebegin case (state) DISC: disc <= 1'd1; // 晚了一个周期! endcase end end
正确做法:
1 2 3 4 5 6 7 8 9 10
// 用next_state判断,刚好在正确的周期输出 always @(posedge clk) begin if (reset) begin disc <= 1'd0; endelsebegin case (next_state) DISC: disc <= 1'd1; endcase end end