关注 望森FPGA 查看更多FPGA资讯
这是望森的第 18 期分享
作者 | 望森
来源 | 望森FPGA
目录
2 Serial receiver and datapath | 串行接收器和数据路径
3 Serial receiver with parity checking | 带奇偶校验的串行接收器
本文中的代码都能够正常运行,请放心食用😋~
练习的官方网站是:https://hdlbits.01xz.net/
注:作者将每个练习的知识点都放在了题目和答案之后
1 Serial receiver | 串行接收器
题目:
在许多(较旧的)串行通信协议中,每个数据字节都与起始位和停止位一起发送,以帮助接收器从位流中划定字节。一种常见的方案是使用一个起始位(0)、8 个数据位和 1 个停止位(1)。当没有传输任何内容(空闲)时,线路也处于逻辑 1。
设计一个有限状态机,当给定一个位流时,它将识别字节何时被正确接收。它需要识别起始位,等待所有 8 个数据位,然后验证停止位是否正确。如果停止位未按预期出现,则 FSM 必须等到找到停止位后才能尝试接收下一个字节。
一些时序图
无错误:
未找到停止位。第一个字节被丢弃:
答案:
1.状态转换图
2.代码
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output done
);
parameter IDLE =5'b0_0001;
parameter START =5'b0_0010;
parameter RD =5'b0_0100;
parameter END =5'b0_1000;
parameter ERROR =5'b1_0000;
reg [4:0] state, next_state;
wire IDLE2IDLE,IDLE2START,
RD2RD,RD2END,RD2ERROR,
END2START,END2IDLE,
ERROR2ERROR,ERROR2IDLE;
always @(posedge clk) begin
if(reset)begin
state <= IDLE;
end
else begin
state <= next_state;
end
end
always @(*) begin
case(state)
IDLE : begin
if (IDLE2IDLE)begin
next_state = IDLE;
end
else if (IDLE2START)begin
next_state = START;
end
end
START : begin
next_state = RD;
end
RD : begin
if (RD2RD)begin
next_state = RD;
end
else if (RD2END)begin
next_state = END;
end
else if (RD2ERROR)begin
next_state = ERROR;
end
end
END : begin
if (END2START)begin
next_state = START;
end
else if (END2IDLE)begin
next_state = IDLE;
end
end
ERROR : begin
if (ERROR2ERROR)begin
next_state = ERROR;
end
else if (ERROR2IDLE)begin
next_state = IDLE;
end
end
default next_state = IDLE;
endcase
end
//RD counter
reg [2:0] cnt_rd;
always @(posedge clk) begin
if(reset)begin
cnt_rd <= 3'd0;
end
else if (state == RD)begin
cnt_rd <= cnt_rd + 1;
end
else begin
cnt_rd <= 3'd0;
end
end
assign IDLE2IDLE = in == 1'b1;
assign IDLE2START = in == 1'b0;
assign RD2RD = cnt_rd < 3'd7;
assign RD2END = in == 1'b1 && cnt_rd == 3'd7;
assign RD2ERROR = in == 1'b0 && cnt_rd == 3'd7;
assign END2START = in == 1'b0;
assign END2IDLE = in == 1'b1;
assign ERROR2ERROR = in == 1'b0;
assign ERROR2IDLE = in == 1'b1;
// Output logic
assign done = state == END;
endmodule
2 Serial receiver and datapath | 串行接收器和数据路径
题目:
现在您有了一个可以识别何时在串行比特流中正确接收字节的有限状态机,请添加一个将输出正确接收的数据字节的数据路径。当 done 为 1 时,out_byte 需要有效,否则无需关心。
请注意,串行协议首先发送最低有效位。
一些时序图
无错误:
答案:
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output reg [7:0] out_byte,
output done
); //
// Use FSM from Fsm_serial
parameter IDLE =5'b0_0001;
parameter START =5'b0_0010;
parameter RD =5'b0_0100;
parameter END =5'b0_1000;
parameter ERROR =5'b1_0000;
reg [4:0] state, next_state;
wire IDLE2IDLE,IDLE2START,
RD2RD,RD2END,RD2ERROR,
END2START,END2IDLE,
ERROR2ERROR,ERROR2IDLE;
always @(posedge clk) begin
if(reset)begin
state <= IDLE;
end
else begin
state <= next_state;
end
end
always @(*) begin
case(state)
IDLE : begin
if (IDLE2IDLE)begin
next_state = IDLE;
end
else if (IDLE2START)begin
next_state = START;
end
end
START : begin
next_state = RD;
end
RD : begin
if (RD2RD)begin
next_state = RD;
end
else if (RD2END)begin
next_state = END;
end
else if (RD2ERROR)begin
next_state = ERROR;
end
end
END : begin
if (END2START)begin
next_state = START;
end
else if (END2IDLE)begin
next_state = IDLE;
end
end
ERROR : begin
if (ERROR2ERROR)begin
next_state = ERROR;
end
else if (ERROR2IDLE)begin
next_state = IDLE;
end
end
default next_state = IDLE;
endcase
end
//RD counter
reg [2:0] cnt_rd;
always @(posedge clk) begin
if(reset)begin
cnt_rd <= 3'd0;
end
else if (state == RD)begin
cnt_rd <= cnt_rd + 1;
end
else begin
cnt_rd <= 3'd0;
end
end
assign IDLE2IDLE = in == 1'b1;
assign IDLE2START = in == 1'b0;
assign RD2RD = cnt_rd < 3'd7;
assign RD2END = in == 1'b1 && cnt_rd == 3'd7;
assign RD2ERROR = in == 1'b0 && cnt_rd == 3'd7;
assign END2START = in == 1'b0;
assign END2IDLE = in == 1'b1;
assign ERROR2ERROR = in == 1'b0;
assign ERROR2IDLE = in == 1'b1;
// Output logic
assign done = state == END;
// New: Datapath to latch input bits.
reg [7:0] byte_reg;
always @(posedge clk) begin
if(state == START)begin
byte_reg <= {in,byte_reg[7:1]};
end
else if(state == RD && cnt_rd < 3'd7)begin
byte_reg <= {in,byte_reg[7:1]};
end
else begin
byte_reg <= byte_reg;
end
end
always @(*) begin
if(done)begin
out_byte = byte_reg;
end
else begin
out_byte = 8'd0;
end
end
endmodule
知识点:
提示:
串行比特流需要一次移入一位,然后并行读出。
注意:进入start状态后就开始寄存输入数据。
3 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 个数据位之后发送奇偶校验位。
一些时序图
无帧错误:
奇校验通过第一个字节,但未通过第二个字节。
答案:
1.状态转换图
2.代码
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
// Use FSM from Fsm_serial
parameter IDLE =6'b00_0001;
parameter START =6'b00_0010;
parameter RD =6'b00_0100;
parameter CHECK =6'b00_1000;
parameter END =6'b01_0000;
parameter ERROR =6'b10_0000;
reg [5:0] state, next_state;
wire IDLE2IDLE,IDLE2START,
RD2RD,RD2CHECK,
CHECK2ERROR,CHECK2END,
END2START,END2IDLE,
ERROR2ERROR,ERROR2IDLE;
reg odd_check;
always @(posedge clk) begin
if(reset)begin
state <= IDLE;
end
else begin
state <= next_state;
end
end
always @(*) begin
case(state)
IDLE : begin
if (IDLE2IDLE)begin
next_state = IDLE;
end
else if (IDLE2START)begin
next_state = START;
end
end
START : begin
next_state = RD;
end
RD : begin
if (RD2RD)begin
next_state = RD;
end
else if (RD2CHECK)begin
next_state = CHECK;
end
end
CHECK : begin
if (CHECK2ERROR)begin
next_state = ERROR;
end
else if (CHECK2END)begin
next_state = END;
end
end
END : begin
if (END2START)begin
next_state = START;
end
else if (END2IDLE)begin
next_state = IDLE;
end
end
ERROR : begin
if (ERROR2ERROR)begin
next_state = ERROR;
end
else if (ERROR2IDLE)begin
next_state = IDLE;
end
end
default next_state = IDLE;
endcase
end
//RD counter
reg [2:0] cnt_rd;
always @(posedge clk) begin
if(reset)begin
cnt_rd <= 3'd0;
end
else if (state == RD)begin
cnt_rd <= cnt_rd + 1;
end
else begin
cnt_rd <= 3'd0;
end
end
assign IDLE2IDLE = in == 1'b1;
assign IDLE2START = in == 1'b0;
assign RD2RD = cnt_rd < 3'd7;
assign RD2CHECK = cnt_rd == 3'd7;
assign CHECK2ERROR = in == 1'b0;
assign CHECK2END = in == 1'b1;
assign END2START = in == 1'b0;
assign END2IDLE = in == 1'b1;
assign ERROR2ERROR = in == 1'b0;
assign ERROR2IDLE = in == 1'b1;
// Output logic
assign done = state == END && odd_check;
// 寄存进入RD状态的前8位数据
reg [7:0] byte_reg;
always @(posedge clk) begin
if(state == START)begin
byte_reg <= {in,byte_reg[7:1]};
end
else if(state == RD && cnt_rd < 3'd7)begin
byte_reg <= {in,byte_reg[7:1]};
end
else begin
byte_reg <= byte_reg;
end
end
//done == 1时输出寄存数据
always @(*) begin
if(done)begin
out_byte = byte_reg;
end
else begin
out_byte = 8'd0;
end
end
//奇偶校验例化
wire reset_parity,odd;
parity parity_inst(
.clk(clk),
.reset(reset_parity || reset),
.in(in),
.odd(odd)
);
//奇偶校验复位
always @(*) begin
if(next_state == START)begin
reset_parity = 1'b1;
end
else begin
reset_parity = 1'b0;
end
end
//奇偶校验结果
always @(*) begin
if(next_state == CHECK)begin
odd_check = odd;
end
else begin
odd_check = odd_check;
end
end
endmodule
知识点:
重点在于控制好奇偶校验的开始时间和结束时间。
4 Sequence recognition | 序列识别
题目:
“同步 HDLC 帧结构”(Synchronous HDLC framing)涉及解码连续的数据位流,以查找指示帧(数据包)开始和结束的位模式。
看到正好 6 个连续的 1(即 01111110)是指示帧边界的“标志”。为了避免数据流意外包含“标志”,发送方会在每 5 个连续的 1 后插入一个零,接收方必须检测并丢弃该零。如果有 7 个或更多连续的 1,我们还需要发出错误信号。
创建一个有限状态机来识别这三个序列:
0111110:发出需要丢弃一个位的信号(disc)。
01111110:标记帧的开始/结束(flag)。
01111111...:错误(7 个或更多 1)(err)。
当 FSM 重置时,它应该处于一种状态,就像之前的输入是 0 一样。
以下是一些示例序列,说明了所需的操作。
丢弃 0111110:
标记 01111110:
重置行为和错误 01111111...:
实现这个状态机。
答案:
1.状态转换图
2.代码
module top_module(
input clk,
input reset, // Synchronous reset
input in,
output disc,
output flag,
output err);
parameter IDLE =10'b00_0000_0001;
parameter S1 =10'b00_0000_0010;
parameter S2 =10'b00_0000_0100;
parameter S3 =10'b00_0000_1000;
parameter S4 =10'b00_0001_0000;
parameter S5 =10'b00_0010_0000;
parameter S6 =10'b00_0100_0000;
parameter DISC =10'b00_1000_0000;
parameter FLAG =10'b01_0000_0000;
parameter ERR =10'b10_0000_0000;
reg [9:0] state, next_state;
wire IDLE2IDLE,IDLE2S1;
wire S12IDLE,S12S2;
wire S22IDLE,S22S3;
wire S32IDLE,S32S4;
wire S42IDLE,S42S5;
wire S52DISC,S52S6;
wire S62FLAG,S62ERR;
wire DISC2IDLE,DISC2S1;
wire FLAG2IDLE,FLAG2S1;
wire ERR2IDLE,ERR2ERR;
always @(posedge clk) begin
if(reset)begin
state <= IDLE;
end
else begin
state <= next_state;
end
end
always @(*) begin
case(state)
IDLE : begin
if (IDLE2IDLE)begin
next_state = IDLE;
end
else if(IDLE2S1)begin
next_state = S1;
end
end
S1 : begin
if (S12IDLE)begin
next_state = IDLE;
end
else if(S12S2)begin
next_state = S2;
end
end
S2 : begin
if (S22IDLE)begin
next_state = IDLE;
end
else if(S22S3)begin
next_state = S3;
end
end
S3 : begin
if (S32IDLE)begin
next_state = IDLE;
end
else if(S32S4)begin
next_state = S4;
end
end
S4 : begin
if (S42IDLE)begin
next_state = IDLE;
end
else if(S42S5)begin
next_state = S5;
end
end
S5 : begin
if (S52DISC)begin
next_state = DISC;
end
else if(S52S6)begin
next_state = S6;
end
end
S6 : begin
if (S62FLAG)begin
next_state = FLAG;
end
else if(S62ERR)begin
next_state = ERR;
end
end
DISC : begin
if (DISC2IDLE)begin
next_state = IDLE;
end
else if(DISC2S1)begin
next_state = S1;
end
end
FLAG : begin
if (FLAG2IDLE)begin
next_state = IDLE;
end
else if(FLAG2S1)begin
next_state = S1;
end
end
ERR : begin
if (ERR2IDLE)begin
next_state = IDLE;
end
else if(ERR2ERR)begin
next_state = ERR;
end
end
default next_state = IDLE;
endcase
end
assign IDLE2IDLE = in == 0;
assign IDLE2S1 = in == 1;
assign S12IDLE = in == 0;
assign S12S2 = in == 1;
assign S22IDLE = in == 0;
assign S22S3 = in == 1;
assign S32IDLE = in == 0;
assign S32S4 = in == 1;
assign S42IDLE = in == 0;
assign S42S5 = in == 1;
assign S52DISC = in == 0;
assign S52S6 = in == 1;
assign S62FLAG = in == 0;
assign S62ERR = in == 1;
assign DISC2IDLE = in == 0;
assign DISC2S1 = in == 1;
assign FLAG2IDLE = in == 0;
assign FLAG2S1 = in == 1;
assign ERR2IDLE = in == 0;
assign ERR2ERR = in == 1;
// Output logic
assign disc = state == DISC;
assign flag = state == FLAG;
assign err = state == ERR;
endmodule
知识点:
提示:
使用具有大约 10 个状态的 Moore 状态机。
- END -
公z号/CSDN搜索【望森FPGA】,查看更多FPGA资讯~
相关推荐文章,点击跳转: