目录
1 串行通信
1.1 serial receiver
在许多(较旧的)串行通信协议中,每个数据字节都与起始位和停止位一起发送,以帮助接收器从位流中分隔字节。一种常见的方案是使用一个起始位(0),8个数据位和1个停止位(1)。当没有任何传输(空闲)时,线路也处于逻辑1。
设计一个有限状态机,当给定比特流时,它将识别何时正确接收了字节。它需要标识起始位,等待所有8个数据位,然后验证停止位是否正确。
如果停止位没有出现在预期的位置,FSM必须等待直到找到停止位,然后才能尝试接收下一个字节。
无错误的情况:
未找到停止位。第一个字节被丢弃:
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output done
);
parameter start=0,data1=1,data2=2,data3=3,data4=4,data5=5,
data6=6,data7=7,data8=8,waitup=9,stop=10,idle=11;
reg [3:0] state,next;
always @(*) begin
case(state)
idle : next <= in ? idle : start;
start : next <= data1;
data1 : next <= data2;
data2 : next <= data3;
data3 : next <= data4;
data4 : next <= data5;
data5 : next <= data6;
data6 : next <= data7;
data7 : next <= data8;
data8 : next <= in ? stop : waitup;
waitup: next <= in ? idle : waitup;
stop : next <= in ? idle : start;
default: next <= idle;
endcase
end
always @(posedge clk) begin
if(reset)
state <= idle;
else
state <= next;
end
assign done = (state==stop);
endmodule
1.2 serial receiver and datapath
既然您已经有了一个有限状态机,可以确定何时在串行位流中正确接收了字节,那么添加一个数据路径,它将输出正确接收的数据字节。当完成为1时,out_byte需要有效,否则不在乎。
请注意,串行协议首先发送最低有效位。
提示:串行位流需要一次移位一位,然后并行读出。
这里的解决方法与这篇文章中的PS/2 packet parser datapath部分的思路一致。均是利用移位寄存器进行存储数据。
solution:
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,data1=1,data2=2,data3=3,data4=4,data5=5,
data6=6,data7=7,data8=8,waitup=9,stop=10,idle=11;
reg [3:0] state,next;
reg [7:0] data;
always @(*) begin
case(state)
idle : next <= in ? idle : start;
start : next <= data1;
data1 : next <= data2;
data2 : next <= data3;
data3 : next <= data4;
data4 : next <= data5;
data5 : next <= data6;
data6 : next <= data7;
data7 : next <= data8;
data8 : next <= in ? stop : waitup;
waitup: next <= in ? idle : waitup;
stop : next <= in ? idle : start;
default: next <= idle;
endcase
end
always @(posedge clk) begin
if(reset)
state <= idle;
else
state <= next;
end
assign done = (state==stop);
// New: Add parity checking.
always @(posedge clk) begin
if (reset) begin
data <= 8'd0;
end
else begin
if (next == data1 || next == data2 || next == data3 || next == data4 || next == data5 || next == data6 || next == data7 || next == data8) begin
data <= {in, data[7:1]};//最后一个进的要在最高位的话,那就in在前
//最后一个进的要在最低位的话,那就in在后,同时data[7:1]要改为data[6:0]
end
end
end
assign out_byte = done ? data : 8'd0;
endmodule
1.3 serial receiver with parity checking
我们要向串行接收器添加奇偶校验(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个数据位之后发送奇偶校验位。
参考这篇文章.
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 idle = 4'd0, start = 4'd1, trans0 = 4'd2,trans1 = 4'd3, trans2 = 4'd4, trans3 = 4'd5;
parameter trans4 = 4'd6, trans5 = 4'd7, trans6 = 4'd8, trans7 = 4'd9, stop = 4'd10, err = 4'd11, pari = 4'd12;
reg [3:0] state, next_state;
reg [7:0] data;
wire odd, reset_p;
reg done_reg;
always @(*) begin
case (state)
idle: next_state <= in ? idle : start;
start: next_state <= trans0;
trans0: next_state <= trans1;
trans1: next_state <= trans2;
trans2: next_state <= trans3;
trans3: next_state <= trans4;
trans4: next_state <= trans5;
trans5: next_state <= trans6;
trans6: next_state <= trans7;
trans7: next_state <= pari;
pari: next_state <= in ? idle : err; //如果停止位没有出现在预期的位置,FSM必须等待直到找到停止位,然后才能尝试接收下一个字节。
err: next_state <= in ? idle : err;
endcase
end
always @(posedge clk) begin
if (reset) begin
state <= idle;
end
else begin
state <= next_state;
end
end
// New: Add parity checking.
always @(posedge clk) begin
if (reset) begin
data <= 8'd0;
reset_p <= 1'b1;
done_reg <= 1'b0;
end
else begin
if (next_state == trans0 || next_state == trans1 || next_state == trans2 || next_state == trans3 || next_state == trans4 || next_state == trans5 || next_state == trans6 || next_state == trans7) begin
data <= {in, data[7:1]};
end
else if (next_state == start) begin
data <= 8'd0;
reset_p <= 1'b0;
done_reg <= 1'b0;
end
else if (next_state == idle) begin
done_reg <= odd;
end
else if (next_state == pari) begin
reset_p <= 1'b1;
end
end
end
assign done = done_reg;
assign out_byte = done ? data : 8'd0;
parity par_mod(clk, reset | reset_p, in, odd);
endmodule
2 sequence recognition
Synchronous HDLC framing涉及对数据的连续位流进行解码,以寻找指示帧(数据包)开始和结束的位模式。 恰好看到6个连续的1(即01111110)是表示帧边界的“flag”。 为了避免数据流意外包含“flag”,发送方必须在每5个连续的1秒后插入一个零,接收方必须检测并丢弃。 如果连续7个或更多1,我们还需要发出错误信号。
创建一个有限状态机来识别这三个序列:
0111110:信号位需要丢弃(disc)。
01111110:标记帧的开始/结束(flag)。
01111111 …:错误(7或更多1s)(err)。
重置FSM时,其状态应类似于先前的输入为0。
以下是一些示例序列,它们说明了所期望的操作。
Discard 0111110:
Flag 01111110:
Reset behaviour and error 01111111…:
提示:
使用一个大约有10个状态的莫尔型状态机。
module top_module(
input clk,
input reset, // Synchronous reset
input in,
output disc,
output flag,
output err);
parameter none=0,one=1,two=2,three=3,four=4,five=5,six=6,error=7,discard=8,f=9;
reg [3:0] state,next;
always @(*) begin
case(state)
none: next = in ? one : none;
one : next = in ? two : none;
two : next = in ? three:none;
three:next = in ? four :none;
four: next = in ? five: none;
five: next = in ? six : discard;
six : next = in ? error : f;
error:next = in ? error : none;
discard:next = in ? one : none;
f: next = in ? one : none;
endcase
end
always @(posedge clk) begin
if(reset)
state <= none;
else
state <= next;
end
assign disc = (state==discard);
assign flag = (state==f);
assign err = (state==error);
endmodule