FPGA基础——串行数据接收(Fsm serialdp)
我们想在串行接收机中加入奇偶校验。奇偶校验检查在每个数据字节后添加一个额外的位。我们将使用奇奇偶校验,其中接收到的9位中的1必须是奇数。例如,101001011个满足奇偶校验(有5个1) ,但001001011个不满足。
只有在正确接收到一个字节并且它的奇偶校验通过时,才断言完成信号。与串行接收机一样,该有限状态机需要确定开始位,等待所有9位(数据和奇偶校验) ,然后验证停止位是正确的。如果停止位没有在预期的时候出现,FSM 必须等到找到停止位之后才能尝试接收下一个字节。
注意,串行协议先发送低有效位,奇偶校验位在8个数据位之后,然后是高有效停止位。
下面是判断输入的8位数据加上奇偶校验位的1的个数的module
module parity (
input clk,
input reset,
input in,
output reg odd);
always @(posedge clk)
if (reset) odd <= 0;
else if (in) odd <= ~odd;
endmodule
下面的是程序的状态图
WAIT表示等待开始位的状态,TRANS表示传输的状态,ODD表示奇偶校验的状态,DONE表示接收到停止位的完成态,CRASH表示未正确接收到停止位的错误态。其中Start_TRANS状态只是一个过度的状态,方便表示一个字节接着一个字节传输,这样从当DONE完成态进入下一个接收的状态的时候,有一个状态方便表示下一个字节开始接受了,odd的值需要清空,同时counter的值也需要清零了。
注: 其实这里的Start_TRANS和TRANS对于state而言是一个状态,因为在next_state变为Start_TRANS之后,会迅速的变为TRANS,所以在下一个时钟周期来临的时候,state就等于TRANS了。
在next_state=WAIT之后,因为in=0,所以会有一个短暂的时间next_state=Start_TRANS,接着next_state就会变为TRANS。所以在下一个时钟沿的时候,取到的next_state的值就是TRANS,所以state就会变为TRANS状态,相当于直接跳过了Start_TRANS状态。
c_state为现在的状态,n_state为下一时钟周期的状态
整体代码如下
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output [7:0] out_byte,
output done
); //
// Modify FSM and datapath from Fsm_serialdata
parameter WAIT = 3'b000,TRANS = 3'b001,ODD = 3'b010,CRASH = 3'b011,DONE = 3'b100,Start_TRANS = 3'b101;
reg [2:0] state,next_state;
reg [3:0] counter;
reg [7:0] out_reg;
always@(posedge clk)begin
if(reset)begin
counter <= 0;
end
else if(counter == 8)begin
counter <= 0;
end
else begin
if(next_state == TRANS)begin // 接收数据,并且设置计数器
counter <= counter + 1;
out_reg[counter] <= in;
end
else begin
counter <= 0;
end
end
end
always@(posedge clk)begin
if(reset)begin
state <= WAIT;
end
else begin
state <= next_state;
end
end
always@(*)begin
case(state)
WAIT: next_state = (in == 1)?WAIT:Start_TRANS;
Start_TRANS: next_state = TRANS; // 过渡态
TRANS: next_state = (counter == 8)?ODD:TRANS;
ODD: next_state = (in == 1)?DONE:CRASH;
CRASH: next_state = (in == 1)?WAIT:CRASH;
DONE: next_state = (in == 1)?WAIT:Start_TRANS;
endcase
end
wire odd;
wire re;
assign re = reset || (next_state == WAIT) ||(next_state == Start_TRANS);
parity parity(clk,re,in,odd);
assign done = (state == DONE) && (~odd); // 因为在输入奇偶校验位的时候,是next_state=DONE的,所以在state=DONE时,这里的odd多记录了一个位置,所以需要反转
assign out_byte = (done)?out_reg:8'bz;
// New: Add parity checking.
endmodule