一、项目需求
二、状态机设计
初始状态IDLE,检测到报文头进入TYPE,检测到控制报文类型进入控制报CONTROL,然后计数器记满64同时输出64个数,进入FCS,计数器记满4个数同时输出4个数后回到IDLE。如果在TYPE检测到数据报文类型进入长度LEN,计数器记满,然后输出len个数据和4个FCS回到IDLE。如果进入TYPE后没有检测到任何报文类型,回到IDLE。
三、代码设计
运用了三段状态机,共有六个状态和七个状态转移。
IDLE>TYPE的状态转移和长度有两个数,用了对第一个数进行打拍然后对两个数进行与和拼接运算。
IDLE的状态转移直接判断din输入的数据。
其他状态转移均可用计数器。
代码如下:
module exam(
input clk ,
input rst_n ,
input [7:0] din ,
output reg [7:0] dout ,
output reg dout_vld
);
localparam
IDLE = 6'b000001,
TYPE = 6'b000010,
CONTROL = 6'b000100,
LEN = 6'b001000,
DATA = 6'b010000,
FCS = 6'b100000;
reg [5:0] state_c ;
reg [5:0] state_n ;
wire IDLE_TYPE ;
wire TYPE_CONTROL;
wire TYPE_LEN ;
wire LEN_DATA ;
wire CONTROL_FCS ;
wire DATA_FCS ;
wire FCS_IDLE ;
wire head_flag1;//判断报文头第一位
wire head_flag2;//判断报文头第二位
reg head_flag3;
assign head_flag1 = (din == 8'h55) ;
assign head_flag2 = (din == 8'hd5) ;
//head_flag1打拍得到head_flag3
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
head_flag3 <= 1'b0 ;
else
head_flag3 <= head_flag1;
end
reg [6:0] control_cnt ;
reg [1:0] len_cnt ;
reg [15:0] data_cnt ;
reg [3:0] fcs_cnt ;
reg [7:0] len1 ;
reg [7:0] len2 ;
reg [7:0] len3 ;
reg [15:0] len0 ;
reg [15:0] len_fin ;
//控制报计数到64bit
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
control_cnt <= 1'b0;
else if(state_c == CONTROL)begin
if(control_cnt == 63)
control_cnt <= 1'b0 ;
else
control_cnt <= control_cnt+1 ;
end
else
control_cnt <= 1'b0 ;
end
//数据报计数到lenbit
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
data_cnt <= 1'b0;
else if(state_c == DATA)begin
if(data_cnt == len_fin-1)
data_cnt <= 1'b0 ;
else
data_cnt <= data_cnt+1 ;
end
else
data_cnt <= 1'b0 ;
end
//FCS计数到4bit
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
fcs_cnt <= 1'b0;
else if(state_c == FCS)begin
if(fcs_cnt == 3)
fcs_cnt <= 1'b0 ;
else
fcs_cnt <= fcs_cnt+1 ;
end
else
fcs_cnt <= 1'b0 ;
end
//len计数到2bit
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
len_cnt <= 1'b0 ;
else if(state_c == LEN)begin
if(len_cnt == 1)
len_cnt <= 1'b0 ;
else
len_cnt <= len_cnt+1 ;
end
else
len_cnt <= 1'b0 ;
end
//len第一位
always @(*) begin
if(state_c == LEN && len_cnt == 0)
len1 = din;
else
len1 = 7'b0 ;
end
//len第二位
always @(*) begin
if(state_c == LEN && len_cnt == 1)
len2 = din;
else
len2 = 7'b0 ;
end
//len1打拍得到len3
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
len3 <= 7'b0 ;
else
len3 <= len1;
end
//len的值
always @(*) begin
len0 = {len3,len2};
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
len_fin <= 14'b0 ;
else if(state_c == LEN)
len_fin <= len0;
else
len_fin <= len_fin ;
end
//状态机七个状态转移条件
assign IDLE_TYPE = (head_flag2 && head_flag3) ;
assign TYPE_CONTROL = (din == 8'ha0) ;
assign TYPE_LEN = (din == 8'h66) ;
assign LEN_DATA = (len_cnt == 1) ;
assign CONTROL_FCS = (control_cnt == 63) ;
assign DATA_FCS = (data_cnt == len_fin-1) ;
assign FCS_IDLE = (fcs_cnt == 3) ;
//状态机第一段
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state_c <= IDLE;
else
state_c <= state_n;
end
//状态机第二段
always @(*) begin
case (state_c)
IDLE : begin
if(IDLE_TYPE)
state_n = TYPE ;
else
state_n = state_c ;
end
TYPE : begin
if(TYPE_CONTROL)
state_n = CONTROL ;
else if(TYPE_LEN)
state_n = LEN ;
else
state_n = state_c ;
end
CONTROL : begin
if(CONTROL_FCS)
state_n = FCS ;
else
state_n = state_c ;
end
LEN : begin
if(LEN_DATA)
state_n = DATA ;
else
state_n = state_c ;
end
DATA : begin
if(DATA_FCS)
state_n = FCS ;
else
state_n = state_c ;
end
FCS : begin
if(FCS_IDLE)
state_n = IDLE ;
else
state_n = state_c ;
end
default: state_n = IDLE;
endcase
end
//状态机第三段
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
dout <= 1'b0 ;
else if(state_c == CONTROL || state_c == DATA || state_c == FCS)
dout <= din ;
else
dout <= dout ;
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
dout_vld <= 1'b0 ;
else if(state_c == CONTROL || state_c == DATA || state_c == FCS)
dout_vld <= 1'b1 ;
else
dout_vld <= 1'b0 ;
end
endmodule
tb文件
timescale 1 ns/1 ns
module exam_tb();
reg clk ;
reg rst_n ;
reg [7:0] din ;
reg [15:0] len ;
wire [7:0] dout ;
wire dout_vld ;
//参数定义
parameter CYCLE = 20;
parameter RST_TIME = 3 ;
//模块例化
exam u_exam(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input [7:0] */.din (din ),
/*output [7:0] */.dout (dout ),
/*output */.dout_vld(dout_vld)
);
initial begin
clk = 1;
forever
#(CYCLE/2)
clk=~clk;
end
initial begin
rst_n = 1;
#2;
rst_n = 0;
#(CYCLE*RST_TIME);
rst_n = 1;
end
initial begin
#1;
din = 0;
#(15*CYCLE);
repeat(5)begin
din = {$random};
#(1*CYCLE);
end
din = 8'h55;
#(1*CYCLE);
din = 8'hd5;//报文头
#(1*CYCLE);
din = 8'ha0;//报文类型 控制报
#(1*CYCLE);
repeat(80)begin //报文数据 + FCS
din = {$random};
#(1*CYCLE);
end
din = 8'h55;
#(1*CYCLE);
din = 8'hd5;//报文头
#(1*CYCLE);
din = 8'h66;//报文类型 数据报
#(1*CYCLE);
din = 8'h00;
#(1*CYCLE);
len = {$random};
din = 8'hf0;//报文长度
#(1*CYCLE);
repeat(len)begin //报文数据
din = {$random};
#(1*CYCLE);
end
repeat(10)begin //FCS
din = {$random};
#(1*CYCLE);
end
$stop;
end
endmodule
四、仿真
当复位后,检测到第一个报头,然后检测到控制报文类型后输出64B的data和4位的FCS,回到IDLE状态重新准备检测报头,检测到第二个报头后,再检测到数据报文类型后,检测到后面两位长度,然后开始输出此长度的数据,数据输出完后输出四位的FCS,然后再次回到IDLE状态。
控制报文和数据报文
控制报文开始
控制报文结束
数据报文开始
数据报文结束
检测到报头后没有找到两种报文返回IDLE状态
可改进的地方
得到len值的部分写的太过复杂,可以简化。