本题相当于是在前两题的基础上进一步升级,见[HDLbits 记录_Q131 Serial receiver]和[HDLbits 记录_Q132 Fsm serialdata],加入了奇偶校验位,关于奇偶校验的知识这里就不详细补充了,简而言之奇校验就是数据区域的所有高电平的个数需要为奇数,偶校验就是数据区域的所有高电平的个数需要为偶数。
本题中正确接收一个8bit数据的流程为:
- 判断起始位
- 接收8bit数据
- 接收校验位
- 判断结束位
其中done 信号的输出条件为正确走完上面的流程并且奇校验值为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
本题的答案也是在前两题的基础上增加逻辑构成的
主要增加的逻辑为:
- 增加了一个ODD状态,用来接收校验位,此处增加一个状态而不是给DATA状态增加一个计数值的目的是让结构更加清晰,另外就是后面移位寄存器的移位也不会受影响(只在DATA状态移位);
- 增加了一个rst信号,用来重置奇校验模块,需要注意置位rst的时机,这里选择的是next_state == IDLE;(举一个我自己的一开始写的错误例子,如果复位条件变为(curr_state == IDLE)&&(next_state == DATA),那么rst会干扰到第一个bit的计算,即在接收第一个bit的时候将模块清零);
- 例化了一个parity模块
直接上代码
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 = 3'd0;
parameter ODD = 3'd1;
parameter DATA = 3'd2;
parameter STOP = 3'd3;
parameter ERROR = 3'd4;
reg [2:0] curr_state;
reg [2:0] next_state;
reg [2:0] dat_cnt;
reg done_r1;
reg rst;
wire done_r;
wire odd;
always @(posedge clk) begin
if(reset==1'b1) begin
curr_state <= IDLE;
end
else begin
curr_state <= next_state;
end
end
always @(*) begin
case(curr_state)
IDLE:begin
if(in == 1'b0) begin
next_state = DATA;
end
else begin
next_state = IDLE;
end
end
DATA:begin
if(dat_cnt == 3'd7) begin
next_state = ODD;
end
else begin
next_state = DATA;
end
end
ODD: begin
next_state = STOP;
end
STOP:begin
if( in == 1'b1) begin
next_state = IDLE;
end
else begin
next_state = ERROR;
end
end
ERROR: begin
if( in == 1'b1) begin
next_state = IDLE;
end
else begin
next_state = ERROR;
end
end
default: next_state= IDLE;
endcase
end
always @(posedge clk) begin
if(reset == 1'b1) begin
dat_cnt <= 3'd0;
end
else if(curr_state == DATA) begin
dat_cnt <= dat_cnt + 1'b1;
end
else begin
dat_cnt <= 3'd0;
end
end
always @(posedge clk) begin
if(reset == 1'b1) begin
done_r <= 1'b0;
end
else begin
done_r <= (curr_state == STOP) && (in == 1'b1) &&(odd == 1'b1);
end
end
assign done = done_r;
// New: Datapath to latch input bits.
reg [7:0] out_reg;
always @(posedge clk) begin
if(reset) begin
out_reg <= 8'd0;
end
else begin
if(curr_state == DATA) begin
out_reg <={in,out_reg[7:1]};//先送低位
end
else begin
out_reg <= out_reg;
end
end
end
assign out_byte = out_reg;
// New: Add parity checking.
always @(posedge clk) begin
if(reset) begin
rst <= 1'b1;
end
else begin
// if((curr_state == IDLE)&&(next_state == DATA)) begin //此复位时机错误,rst会干扰到第一个bit的计算
if(next_state == IDLE) begin
rst <= 1'b1;
end
else begin
rst <=1'b0;
end
end
end
parity inst_parity(
.clk(clk),
.reset(rst),
.in(in),
.odd(odd));
endmodule