这是博主在刷HDLbits卡住很久的一道题,在这里记录下来,自己总结下思路,同时也希望能给大家提供参考。
一、设计要求
在许多串行通信协议中,每个数据字节都与一个起始位和一个停止位一起发送,以帮助接收器从位流中划定字节。一种常见的方案是使用 1 个起始位 (0)、8 个数据位和 1 个停止位 (1)。当没有传输任何内容(空闲)时,该线路也处于逻辑 1。
要求: 设计一个有限状态机,当给定比特流时,它将识别何时正确接收到字节。它需要识别起始位,等待所有 8 个数据位,然后验证停止位是否正确。如果停止位未按预期出现,则 FSM 必须等到找到停止位后再尝试接收下一个字节。
我们要实现的时序图如下。
二、思路分析
这道题是用状态机来实现,所以重要的是如何设计状态的状态和状态的转换。这里,博主并没有使用题目中时序图给的状态。经过对题目的分析:
- 传输不出错的时候:也就是起始位为0,结束位为1。这个时候传输结束了有两种状态,连续传输和暂停一下继续传输。
- 传输出错的时候:也就是停止位不为1。这个时候需要等待in变到高位,进入空闲状态,然后才能进行新的数据的传输。
我设计了状态转移框图如下:
- IDLE:空闲状态,只要in为0,就开始进行传输。当复位、完成一个字节数据传输需要等待、未完成传输需要等待下一个数据传输的时候,进入该状态。
- TRANS:进行数据传输。需要一个计数器来计算已经传输的位数,当传输8位后要判定结束位。结束位有效就完成传输,否则丢弃该数据,进入WAIT状态。
- DONE:完成一个数据的传输,可以继续传输,或者等待。
- WAIT:当传输的数据无效的时候,需要丢掉该数据,并进入WAIT状态。当in=1时,进入IDLE状态,准备进行下一个数据的传输。
三、代码实现
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output done
);
parameter IDLE = 2'd00, TRANS = 2'd01,DONE = 2'd10,WAIT = 2'd11;
reg [1:0] state,next_state;
reg [5:0] counter;
//计数器
always @(posedge clk) begin
if (reset) begin
counter <= 6'd0;
end
else begin
if (state == TRANS)begin
counter <= counter + 1'd1;
end
else begin
counter <= 6'd0;
end
end
end
//状态转移逻辑
always @(*) begin
case(state)
IDLE:next_state <= in ? IDLE:TRANS;
TRANS: begin
if (counter == 6'd8) begin
if (in == 1'd1) begin
next_state <= DONE;
end
else begin
next_state <= WAIT;
end
end
else begin
next_state <= TRANS;
end
end
WAIT: next_state <= in ? IDLE : WAIT;
DONE: next_state <= in ? IDLE : TRANS;
endcase
end
always @(posedge clk ) begin
if (reset) begin
state <= IDLE;
end
else begin
state <= next_state;
end
end
//输出
assign done = (state == DONE) ;
endmodule