-
Notifications
You must be signed in to change notification settings - Fork 1
/
SerialReceiver.v
242 lines (186 loc) · 6.57 KB
/
SerialReceiver.v
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
Basic serial receiver (UART)
Copyright (c) 2020 Stanislav Jurny (github.com/STjurny) license MIT
Serial data format is 8 data bits, without parity, one stop bit (8N1) without hardware flow control.
Set parameters pClockFrequency and pBaudRate to requirements of your design (pBaudRate can be max 1/4 of
pClockFrequency). For high baud rates check values of parametrization report pInaccuracyPerFrame and
pInaccuracyThreshhold to ensure that ClockFrequency / BaudRate ratio generates acceptable inaccuracy of frame length.
Generally pInaccuracyPerFrame have to be less than pInaccuracyThreshhold. For ideal ratio is pInaccuracyPerFrame = 0.
Each time receiver receives one valid frame (byte) it makes it available in oData and set oReceived to 1 for one clock.
If a break or a missing stop bit error occurs in receiving serial data oBreak is set to 1 for one clock.
Module supports automatic power on reset (after load bitstream to the FPGA), explicit reset over iReset signal or both
of them. Mode of reset is determined by preprocessor symbols GlobalReset and PowerOnReset. Edit the Global.inc file
to select reset modes.
*/
`include "Global.inc"
module SerialReceiver #(
parameter pClockFrequency = 16000000,
//^ System clock frequency.
parameter pBaudRate = 115200
//^ Serial input baud rate (..., 9600, 115200, 2000000, ...)
//^ Can be value from arbitrary low to max 1/4 of pClockFrequency.
)(
input wire iClock,
//^ System clock with frequency specified in the parameter pClockFrequency.
input wire iRxd,
//^ Serial data input with baud rate specified in pBaudRate parameter
output wire [7:0] oData,
//^ Received data byte (valid for one clock when oReceived is 1).
output wire oReceived,
//^ Signalizes (for one clock) received valid data in oData.
output wire oBreak
//^ Signalizes (for one clock) break or missing stop bit error in receiving serial data.
`ifdef GlobalReset
,input wire iReset
//^ Reset module to initial state (reset is synchronized with posedge, set to 1 for one clock is enough).
//^ After reset the module waits for transition iRxd to 1 and then it begins to wait for serial data.
`endif
);
localparam
pTicksPerBit = pClockFrequency / pBaudRate,
pTicksPerBitAndHalf = (pClockFrequency + pClockFrequency / 2) / pBaudRate,
pTicksPerFrame = pClockFrequency * 10 / pBaudRate;
localparam
pInaccuracyPerFrame = pTicksPerFrame - pTicksPerBit * 10,
pInaccuracyThreshhold = pTicksPerBit / 2;
localparam
pRawDelayToFirstBit = pTicksPerBitAndHalf - 1 - 1, // - zero_tick - idle_tick
pRawDelayToNextBit = pTicksPerBit - 1 - 1; // - zero_tick - sample_tick
localparam
pTimerMsb = $clog2(pRawDelayToFirstBit + 1) - 1,
pDelayToFirstBit = pRawDelayToFirstBit[pTimerMsb:0],
pDelayToNextBit = pRawDelayToNextBit[pTimerMsb:0];
initial // parametrization report
begin
$display("%m|1|--");
`ifdef GlobalReset
$display("%m|1|GlobalReset = yes");
`else
$display("%m|1|GlobalReset = no");
`endif
`ifdef PowerOnReset
$display("%m|1|PowerOnReset = yes");
`else
$display("%m|1|PowerOnReset = no");
`endif
$display("%m|1|pClockFrequency = '%d", pClockFrequency);
$display("%m|1|pBaudRate = '%d", pBaudRate);
$display("%m|1|--");
$display("%m|1|pTicksPerBit = '%d", pTicksPerBit);
$display("%m|1|pTicksPerBitAndHalf = '%d", pTicksPerBitAndHalf);
$display("%m|1|pTicksPerFrame = '%d", pTicksPerFrame);
$display("%m|1|--");
$display("%m|1|pInaccuracyPerFrame = '%d", pInaccuracyPerFrame);
$display("%m|1|pInaccuracyThreshhold = '%d", pInaccuracyThreshhold);
$display("%m|1|--");
$display("%m|1|pDelayToFirstBit = '%d", pDelayToFirstBit);
$display("%m|1|pDelayToNextBit = '%d", pDelayToNextBit);
$display("%m|1|cTimer range = '%d:0", pTimerMsb);
if (pTicksPerBit < 4)
begin
$display("%m|0|Error: Parameter pBaudrate can be max 1/4 of clock frequency.");
$stop();
end
end
localparam // $State:3,st
stErrorRecovery = 0, // initial state must be zero
stIdle = 1,
stReadDataBit = 2,
stWaitToDataBit = 3,
stWaitToStopBit = 4,
stCheckStopBit = 5;
reg [2:0] cState;
reg cRxdSyncPipe;
reg cRxd;
reg cReceived; assign oReceived = cReceived;
reg cBreak; assign oBreak = cBreak;
reg [7:0] cData; assign oData = cData;
reg [2:0] cBitCounter;
reg [pTimerMsb:0] cTimer;
reg cTimerIsZero;
`ifdef PowerOnReset
initial
begin
cState = stErrorRecovery;
cRxd = 0;
cRxdSyncPipe = 0;
cBitCounter = 0;
cReceived = 0;
cBreak = 0;
end
`endif
always @(posedge iClock)
`ifdef GlobalReset
if (iReset)
begin
cState <= stErrorRecovery;
cRxd <= 0;
cRxdSyncPipe <= 0;
cBitCounter <= 0;
cReceived <= 0;
cBreak <= 0;
end
else
`endif
begin
// synchronization iRxd input to avoid metastability issues
cRxdSyncPipe <= iRxd;
cRxd <= cRxdSyncPipe;
// comparison is potentially complex so we do it separately one clock earlier
cTimerIsZero <= cTimer == 1;
case (cState)
stErrorRecovery:
begin
cBreak <= 0;
if (cRxd)
cState <= stIdle;
end
stIdle:
begin
cReceived <= 0;
if (~cRxd)
cState <= stWaitToDataBit;
cTimer <= pDelayToFirstBit;
end
stWaitToDataBit:
if (!cTimerIsZero)
cTimer <= cTimer - 1;
else
cState <= stReadDataBit;
stReadDataBit:
begin
cData <= {cRxd, cData[7:1]};
cBitCounter <= cBitCounter + 1;
if (cBitCounter != 7)
cState <= stWaitToDataBit;
else
cState <= stWaitToStopBit;
cTimer <= pDelayToNextBit;
end
stWaitToStopBit:
if (!cTimerIsZero)
cTimer <= cTimer - 1;
else
cState <= stCheckStopBit;
stCheckStopBit:
if (cRxd)
begin
cReceived <= 1;
cState <= stIdle;
end
else
begin
cBreak <= 1;
cState <= stErrorRecovery;
end
default:
begin
`ifdef Simulation
$display("%m|0|Illegal state");
$stop();
`endif
cState <= 3'bX;
end
endcase
end
endmodule