3.2.5.15 PS/2 packet parser
问题陈述:
PS/2鼠标协议发送三个字节长的消息。然而,在连续的字节流中,消息的开始和结束位置并不明显。唯一的指示是,每个三字节消息的第一个字节总是bit[3]=1(但其他两个字节的bit[3]可能是1或0,这取决于数据)。
我们想要一个有限状态机,在给定输入字节流时,搜索消息边界。我们将使用的算法是丢弃字节,直到看到bit[3]=1为止。然后我们假设这是消息的第1字节,并在所有3个字节都被接收(完成)后发出接收消息的信号。FSM应该在成功接收到每个消息的第三个字节后立即在周期内发出信号。
一些时序来解释所需的行为
在无错误的情况下,每三个字节形成一条消息:
发生错误时,搜索字节1:
请注意,这1xx序列识别器不同。此处不允许重叠序列:
Verilog代码:
module top_module(
input clk,
input [7:0] in,
input reset, // Synchronous reset
output done); //
parameter s0=0,s1=1,s2=2,s3=3;
reg [1:0] state ,next;
always@(*) begin
case (state)
s0: next=in[3]?s1:s0;
s1: next=s2;
s2: next=s3;
s3: next=in[3]?s1:s0;
endcase
end
always@(posedge clk) begin
if(reset)
state<=s0;
else
state<=next;
end
assign done=(state==s3);
endmodule
3.2.5.16 PS/2 packet parser and datapath
问题陈述:
现在,您有了一个状态机,它将识别PS/2字节流中的3字节消息,添加一个数据路径,它也将在接收到数据包时输出24位(3字节)消息(out_bytes[23:16]是第一个字节,out_bytes[15:8]是第二个字节,等等)。
每当断言done信号时,Out_bytes需要是有效的。您可以在其他时间输出任何内容(即,不关心)。
例如:
Verliog代码:
module top_module(
input clk,
input [7:0] in,
input reset, // Synchronous reset
output [23:0] out_bytes,
output done); //
// FSM from fsm_ps2
parameter s0=2'b00,s1=2'b01,s2=2'b10,s3=2'b11;
reg [1:0] state ,next;
always@(*) begin
case (state)
s0: next=in[3]?s1:s0;
s1: next=s2;
s2: next=s3;
s3: next=in[3]?s1:s0;
endcase
end
always@(posedge clk) begin
if(reset)
state<=s0;
else
state<=next;
end
assign done=(state==s3);
// New: Datapath to store incoming bytes.
always@(posedge clk)
begin
case(state)
s0: out_bytes[23:16]<=in;
s1: out_bytes[15:8]<=in;
s2: out_bytes[7:0]<=in;
s3: out_bytes[23:16]<=in;
endcase
end
endmodule
3.2.5.17 Serial receiver
问题陈述:
在许多(较老的)串行通信协议中,每个数据字节都随一个起始位和一个停止位一起发送,以帮助接收方从位流中划定字节。一种常见的方案是使用1个开始位(0)、8个数据位和1个停止位(1)。当没有任何传输(空闲)时,线路也处于逻辑1。
设计一个有限状态机,当给定比特流时,它将识别字节是否被正确接收。因此,它需要识别起始位,等待所有8个数据位,然后验证停止位是否正确。如果停止位未按预期出现,则FSM必须等到它找到一个停止位后再尝试接收下一个字节。
一些时序图
无错误:
未找到停止位。第一个字节被丢弃:
Verilog代码:
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output done
);
parameter start=0,s1=1,s2=2,s3=3,s4=4,s5=5,s6=6,s7=7,s8=8,stop=9,idle=10,Wait=11;
reg [3:0] state,next;
always@(*) begin
case(state)
start: next= in? start:s1;
s1: next=s2;
s2: next=s3;
s3: next=s4;
s4: next=s5;
s5: next=s6;
s6: next=s7;
s7: next=s8;
s8: next=stop;
stop: next=in? idle:Wait;
idle: next=in?start:s1;
Wait:next=in? start:Wait;
endcase
end
always@(posedge clk) begin
if(reset)
state<=start;
else
state<=next;
end
assign done=(state==idle);
endmodule
3.2.5.17 Serial receiver and datapath
问题陈述:
现在你有一个有限状态机,可以识别何时在串行比特流中正确接收到字节,添加一个数据路径来输出正确收到的数据字节。out_byte需要在done 为1时有效,否则不在乎。
请注意,串行协议首先发送最低有效位。
一些时序图
无错误:
Verilog代码:
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output [7:0] out_byte,
output done
); //
// Use FSM from Fsm_serial
parameter start=0,s1=1,s2=2,s3=3,s4=4,s5=5,s6=6,s7=7,s8=8,stop=9,idle=10,Wait=11;
reg [3:0] state,next;
reg [7:0]temp;
always@(*) begin
case(state)
start: next= in? start:s1;
s1: next=s2;
s2: next=s3;
s3: next=s4;
s4: next=s5;
s5: next=s6;
s6: next=s7;
s7: next=s8;
s8: next=stop;
stop: next=in? idle:Wait;
idle: next=in?start:s1;
Wait:next=in? start:Wait;
endcase
end
always@(posedge clk) begin
if(reset)
state<=start;
else
state<=next;
end
assign done=(state==idle);
// New: Datapath to latch input bits.
always@(posedge clk) begin
case(state)
s1:temp[0]<=in;
s2:temp[1]<=in;
s3:temp[2]<=in;
s4:temp[3]<=in;
s5:temp[4]<=in;
s6:temp[5]<=in;
s7:temp[6]<=in;
s8:temp[7]<=in;
endcase
end
assign out_byte=done?temp:8'b0;
endmodule
3.2.5.18 Serial receiver with parity checking
问题陈述:
我们想在串行接收器中添加奇偶校验。奇偶校验在每个数据字节后增加一位。我们将使用奇校验,其中接收的9位中的1的数量必须为奇数。例如,101001011满足奇偶校验(有5个1),而001001011不满足奇偶校验。
修改FSM和数据路径以执行奇偶校验。只有当一个字节被正确接收并且其奇偶校验通过时,才断言完成的信号。像串行接收器FSM,这个FSM需要识别起始位,等待所有9位(数据和奇偶校验),然后验证停止位是否正确。如果停止位没有出现,FSM必须等待,直到它找到一个停止位,然后再尝试接收下一个字节。您可以使用以下模块来计算输入流的奇偶校验(它是一个带复位的TFF)。预期的用途是,应该给它输入位流,并在适当的时间重置,以便它计算每个字节中1位的数量。
module parity (
input clk,
input reset,
input in,
output reg odd);
always @(posedge clk)
if (reset) odd <= 0;
else if (in) odd <= ~odd;endmodule
请注意,串行协议先先发送最低有效位,然后再发送8个数据位之后的奇偶校验位。
一些时序图
没有构图错误。第一个字节校验通过,第二个字节失败。
Verilog代码:
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output [7:0] out_byte,
output done
); //
parameter idle=0,start=1,s0=2,s1=3,s2=4,s3=5,s4=6,s5=7,s6=8,s7=9,s8=10,stop=11,WAIT=12;
wire[3:0] state,next;
wire odd_bit;
reg odd;
wire res;
always @(*)
begin
case(state)
idle: next=in? idle:start;
start:next=s0;
s0:next=s1;
s1:next=s2;
s2:next=s3;
s3:next=s4;
s4:next=s5;
s5:next=s6;
s6:next=s7;
s7:next=s8;
s8:next=in?stop:WAIT;
stop: next=in? idle:start;
WAIT: next=in? idle:WAIT;
endcase
end
always @(posedge clk)
begin
if(reset)
state<=idle;
else
state<=next;
end
assign done=(odd==0&&state==stop);
always@(posedge clk)
begin
case(state)
start:out_byte[0]<=in;
s0:out_byte[1]<=in;
s1:out_byte[2]<=in;
s2:out_byte[3]<=in;
s3:out_byte[4]<=in;
s4:out_byte[5]<=in;
s5:out_byte[6]<=in;
s6:out_byte[7]<=in;
s7: odd_bit<=in;
default: out_byte<=out_byte;
endcase
end
always@(*)
begin
if(state==idle||state==stop)
res<=1;
else
res<=0;
end
parity u0(clk,res,in,odd);
endmodule
3.2.5.19 Sequence recognition
问题陈述:
同步HDLC成帧涉及对数据的连续比特流进行解码,以寻找指示帧(数据包)开始和结束的比特模式。恰好看到六个连续的1 (即01111110)是指示帧边界的“标志”,发送方在5个连续的1后插入一个零,接收方必须检测并丢弃该0。如果有7个或者更多连续的1,我们还需要发出错误信号。
创建一个有限状态机来识别这三个序列:0111110: 需要丢弃信号位(disc)。
01111110: 标记帧的开始/结束(flag)。
01111111…: 错误(7个或更多1s)(err)
当FSM被重置时,应该处于一个状态,就像之前的输入为0一样。
一下是一些说明所需操作的示例序列。
丢弃0111110:
标志01111110:
重置行为和错误01111111…:
Verilog代码:
module top_module(
input clk,
input reset, // Synchronous reset
input in,
output disc,
output flag,
output err);
parameter s0=0,s1=1,s2=2,s3=3,s4=4,s5=5,s6=6,ERROR=7,DISC=8,FLAG=9;
wire [3:0] state,next;
always@(posedge clk)
begin
if(reset)
state<=s0;
else
state<=next;
end
always@(*)
begin
case(state)
s0:next=in?s1:s0;
s1:next=in?s2:s0;
s2:next=in?s3:s0;
s3:next=in?s4:s0;
s4:next=in?s5:s0;
s5:next=in?s6:DISC;
s6:next=in?ERROR:FLAG;
ERROR:next=in?ERROR:s0;
DISC:next=in?s1:s0;
FLAG:next=in?s1:s0;
endcase
end
assign err=(state==ERROR);
assign flag=(state==FLAG);
assign disc=(state==DISC);
endmodule