Menu Close

uart通信rx模块在FPGA实现的一种Verilog状态机写法(四)

上一篇文章里,我们介绍了rx模块在参数固定时后的写法,其实主要工作已经完成,剩下的就是对模块的扩展和完善。扩展和完善工作也不复杂,只要对uart几个可变参数进行变量化或参数化即可。在之前的文章中我们也曾这样做过,感兴趣的朋友可以也可以看一下之前tx端的写法(https://www.icfedu.cn/archives/3478)。下面我们按照同样的做法,完善一下rx模块。
%title插图%num
uart 字符帧结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
`define NONE    0
`define ODD     1
`define EVEN    2
`define MARK    3
`define SPACE   4

`define STOPBIT_ONE         0
`define STOPBIT_ONEHALF     1
`define STOPBIT_TWO         2


module uart_rx #(
parameter   BAUD            =   9600,
parameter   CLK_F           =   50_000_000,
parameter   PARITY          =   `NONE,
parameter   DATA_BITS       =   8,
parameter   STOPBIT         =   `STOPBIT_ONE

)(
input           wire                            clk,
input           wire                            rst,
input           wire                            rx,

output          reg [DATA_BITS - 1:0]           data,
output          reg                             parity_err, //接收校验错误
output          reg                             done        //接收完成提示

);

//【1】检测起始位
reg [1:0]   rx_r;

always@(posedge clk or posedge rst) begin
if(rst)
    rx_r<=0;
else
    rx_r<={rx_r[0],rx};
end

wire en;        //起始位信号
assign en = (rx_r==2'b10) ? 1'b1 : 1'b0;


//【2】使用状态机来接收rx线上的数据
localparam          CNT_MAX         =       CLK_F / BAUD;//接收bit位计数(时钟周期个数)上限值


//[1] 定义状态空间
localparam          FSM_IDLE        =       0;  //空闲状态
localparam          FSM_START       =       1;  //接收起始位状态
localparam          FSM_DATA        =       2;  //接收数据位
localparam          FSM_PARITY      =       3;  //接收校验位
localparam          FSM_STOP        =       4;  //接收停止位

//[2] 定义状态变量
reg                 [2:0]                   cstate;



//[3] 实现状态机
//每个状态做状态判断和跳转,以及根据状态输出

reg                 [DATA_BITS-1:0]          data_r;     //暂存接收到的数据
reg                 [31:0]                   cnt;        //时钟周期计数,上限就是CNT_MAX
reg                                          startbit;   //接收到的起始位值
reg                 [3:0]                    num;        //接收到的数据位个数


always@(posedge clk or posedge rst) begin
if(rst)
    begin
        cstate <= 0;
        data_r <= 0;
        num <= 0;
        done <= 0;
        cnt <= 0;
        startbit <= 0;
        parity_err <=0;
    end
else
    case(cstate)
        FSM_IDLE    :   begin
                            done <= 0;
                            if(en)
                                begin
                                    cstate <= FSM_START;
                                    cnt <= 0;
                                end
                            else
                                cstate <= FSM_IDLE;
                        end

        FSM_START   :   begin
                            cnt>1) )        //在bit位中点时进行采样
                                startbit  (CNT_MAX>>1) && startbit)
                                cstate <= FSM_IDLE ;        //收到开始位为1 ,说明前面是个干扰信号
                            else if(startbit == 0 && cnt == CNT_MAX - 1 )
                                begin
                                    cstate <= FSM_DATA;     //收到开始位为0 ,是起始位
                                    cnt<=0;
                                end
                        end

        FSM_DATA    :   begin
                            cnt>1) )            //在bit位中点时进行采样
                                data_r[num] <= rx_r[1];

                            if(cnt == CNT_MAX - 1 && num <= DATA_BITS-1)//接收bit位少于DATA_BITS时继续接收
                                begin
                                    num <= num +1 ;
                                    cnt <= 0;
                                end
                            else if(num == DATA_BITS)   //接收bit位大于DATA_BITS时跳转
                                                        //写的明明是==,为何说大于?因为bit位数为8,计数应该是0-7
                                begin
                                    cstate <= ifparity ? FSM_PARITY : FSM_STOP;//有无校验位的判断
                                    cnt <=0;
                                    num<=0;
                                end
                        end
                           
        FSM_PARITY  :   begin
                            cnt >1) )
                                begin
                                    if(parity_value == rx_r[1] )//计算出的校验位的值 和 发送过来的校验位是否相等?
                                        parity_err <= 1'b0;
                                    else
                                        parity_err  (CNT_MAX>>1) )
                                parity_err <= 1'b0;             //保证奇偶校验错误也只发单周期的高电平

                            if(cnt == CNT_MAX - 1)
                                begin
                                    cstate <= FSM_STOP;
                                    cnt<=0;
                                end
                        end

        FSM_STOP    :   begin
                            cnt <= cnt + 1;

                            case(STOPBIT)               //不同长度停止位的处理
                            `STOPBIT_ONE        :   if(cnt == CNT_MAX -1 )      //1停止位
                                                        begin
                                                            cstate <= FSM_IDLE;
                                                            data <= data_r;
                                                            done <= 1;
                                                            cnt <= 0;
                                                        end
                                                    else
                                                        cstate >1) -1 )//1.5停止位
                                                        begin
                                                            cstate <= FSM_IDLE;
                                                            data <= data_r;
                                                            done <= 1;
                                                            cnt <= 0;
                                                        end
                                                    else
                                                       cstate <= FSM_STOP;
                                                           
                            `STOPBIT_TWO        :   if(cnt == (CNT_MAX<<1 ) -1 )        //2停止位
                                                        begin
                                                            cstate <= FSM_IDLE;
                                                            data <= data_r;
                                                            done <= 1;
                                                            cnt <= 0;
                                                        end
                                                    else
                                                        cstate <= FSM_STOP;
                                                           
                            default             :   if(cnt == CNT_MAX -1 )
                                                        begin
                                                            cstate <= FSM_IDLE;
                                                            data <= data_r;
                                                            done <= 1;
                                                            cnt <= 0;
                                                        end
                                                    else
                                                        cstate <= FSM_STOP;
                            endcase
                        end
       
        default     :   begin
                            cstate <= 0;
                            data_r <= 0;
                            num <= 0;
                            done <= 0;
                            cnt <= 0;
                            startbit <= 0;
                            parity_err <=0;
                        end
    endcase

end

//校验位
reg ifparity;                           //是否有检验位
reg parity_value;                       //校验位的值

always@(*)  begin
if(rst)
    begin   ifparity = 0 ; parity_value = 0 ; end
else
    case(PARITY)
        `NONE           :   begin   ifparity = 0 ; parity_value = 0 ; end
        `ODD            :   begin   ifparity = 1 ; parity_value = ~(^data_r) ; end
        `EVEN           :   begin   ifparity = 1 ; parity_value = ^data_r ; end
        `MARK           :   begin   ifparity = 1 ; parity_value = 1 ; end
        `SPACE          :   begin   ifparity = 1 ; parity_value = 0 ; end
        default         :   begin   ifparity = 0 ; parity_value = 0 ; end
    endcase
end
endmodule
Posted in FPGA, Verilog, Verilog

发表评论

相关链接