1 |
2 |
sinclairrf |
################################################################################
|
2 |
|
|
#
|
3 |
6 |
sinclairrf |
# Copyright 2013-2014, Sinclair R.F., Inc.
|
4 |
2 |
sinclairrf |
#
|
5 |
|
|
################################################################################
|
6 |
|
|
|
7 |
|
|
import math;
|
8 |
|
|
import re;
|
9 |
|
|
|
10 |
|
|
from ssbccPeripheral import SSBCCperipheral
|
11 |
|
|
from ssbccUtil import IsPowerOf2;
|
12 |
|
|
from ssbccUtil import SSBCCException;
|
13 |
|
|
|
14 |
|
|
class UART(SSBCCperipheral):
|
15 |
|
|
"""
|
16 |
|
|
Transmit/receive UART:
|
17 |
|
|
1 start bit
|
18 |
|
|
8 data bits
|
19 |
|
|
1 or 2 stop bits\n
|
20 |
|
|
Usage:
|
21 |
|
|
PERIPHERAL UART inport=I_inport_name \\
|
22 |
|
|
outport=O_outport_name \\
|
23 |
|
|
inempty=I_inempty_name \\
|
24 |
|
|
outstatus=I_outstatus_name \\
|
25 |
|
|
baudmethod={clk/rate|count} \\
|
26 |
|
|
[insignal=i_name] \\
|
27 |
|
|
[outsignal=o_name] \\
|
28 |
|
|
[noSync|sync=n] \\
|
29 |
|
|
[noDeglitch|deglitch=n] \\
|
30 |
|
|
[noInFIFO|inFIFO=n] \\
|
31 |
|
|
[noOutFIFO|outFIFO=n] \\
|
32 |
6 |
sinclairrf |
[{CTS|CTSn}=i_cts_name] \\
|
33 |
|
|
[{RTR|RTRn}=i_rtr_name] \\
|
34 |
|
|
[nStop={1|2}]\n
|
35 |
2 |
sinclairrf |
Where:
|
36 |
|
|
inport=I_inport_name
|
37 |
|
|
specifies the symbol used by the inport instruction to read a received by
|
38 |
|
|
from the peripheral
|
39 |
|
|
Note: The name must start with "I_".
|
40 |
|
|
outport=O_outport_name
|
41 |
|
|
specifies the symbol used by the outport instruction to write a byte to
|
42 |
|
|
the peripheral
|
43 |
|
|
Note: The name must start with "O_".
|
44 |
|
|
inempty=I_inempty_name
|
45 |
|
|
specifies the symbol used by the inport instruction to get the empty
|
46 |
|
|
status of the input side of the peripheral
|
47 |
|
|
Note: The name must start with "I_".
|
48 |
|
|
outstatus=I_outstatus_name
|
49 |
|
|
specifies the symbol used by the inport instruction to get the status of
|
50 |
|
|
the output side of the peripheral
|
51 |
|
|
Note: The name must start with "I_".
|
52 |
|
|
baudmethod
|
53 |
|
|
specifies the method to generate the desired bit rate:
|
54 |
|
|
1st method: clk/rate
|
55 |
|
|
clk is the frequency of "i_clk" in Hz
|
56 |
|
|
a number will be interpreted as the clock frequency in Hz
|
57 |
|
|
a symbol will be interpreted as a parameter
|
58 |
|
|
Note: this parameter must have been declared with a "PARAMETER"
|
59 |
|
|
command
|
60 |
|
|
rate is the desired baud rate
|
61 |
|
|
this is specified as per "clk"
|
62 |
|
|
2nd method:
|
63 |
|
|
specify the number of "i_clk" clock cycles between bit edges
|
64 |
|
|
Note: clk, rate, and count can be parameters or constants. For example,
|
65 |
|
|
the following uses the parameter G_CLK_FREQ_HZ for the clock
|
66 |
|
|
frequency and a hard-wired baud rate of 9600:
|
67 |
|
|
"baudmethod=G_CLK_FREQ_HZ/9600".
|
68 |
|
|
Note: The numeric values can have Verilog-style '_' separators between
|
69 |
|
|
the digits. For example, 100_000_000 represents 100 million.
|
70 |
|
|
insignal=i_name
|
71 |
|
|
optionally specifies the name of the single-bit transmit signal
|
72 |
|
|
Default: i_UART_Rx
|
73 |
|
|
outsignal=o_name
|
74 |
|
|
optionally specifies the name of the output signal
|
75 |
|
|
Default: o_UART_Tx
|
76 |
|
|
noSync
|
77 |
|
|
optionally state no synchronization or registration is performed on the
|
78 |
|
|
input signal.
|
79 |
|
|
sync=n
|
80 |
|
|
optionally state that an n-bit synchronizer will be performed on the
|
81 |
|
|
input signal.
|
82 |
|
|
Note: sync=3 is the default.
|
83 |
|
|
noDeglitch
|
84 |
|
|
optionally state that no deglitching is performed on the input signal.
|
85 |
|
|
Note: This is the default.
|
86 |
|
|
deglitching=n
|
87 |
|
|
optionally state that an n-bit deglitcher is performed on the input signal
|
88 |
|
|
Note: Deglitching consists of changing the output state when n
|
89 |
|
|
successive input bits are in the opposite state.
|
90 |
|
|
noInFIFO
|
91 |
|
|
optionally state that the peripheral will not have an input FIFO
|
92 |
|
|
Note: This is the default.
|
93 |
|
|
inFIFO=n
|
94 |
|
|
optionally add a FIFO of depth n to the input side of the UART
|
95 |
|
|
Note: n must be a power of 2.
|
96 |
|
|
noOutFIFO
|
97 |
|
|
optionally state that the peripheral will not have an output FIFO
|
98 |
|
|
Note: This is the default.
|
99 |
|
|
outFIFO=n
|
100 |
|
|
optionally add a FIFO of depth n to the output side of the UART
|
101 |
|
|
Note: n must be a power of 2.
|
102 |
6 |
sinclairrf |
CTS=i_cts_name or CTSn=i_cts_name
|
103 |
|
|
optionally specify an input handshake signal to control whether or not the
|
104 |
|
|
peripheral transmits data
|
105 |
|
|
Note: If CTS is specified then the transmitter is active when i_cts_name
|
106 |
|
|
is high. If CTSn is specified then the transmitter is active when
|
107 |
|
|
i_cts_name is low.
|
108 |
|
|
Note: The default, i.e., neither CTS nor CTSn is specified, is to always
|
109 |
|
|
enable the transmitter.
|
110 |
|
|
Note: If there is no FIFO and the CTS/CTSn handshake indicates that the
|
111 |
|
|
data flow is disabled, then the busy signal will be high and the
|
112 |
|
|
processor code must not transmit the next byte.
|
113 |
|
|
RTR=i_rtr_name or RTRn=i_rtr_name
|
114 |
|
|
optionally specify an output handshake signal to indicate that the
|
115 |
|
|
peripheral is ready to receive data
|
116 |
|
|
Note: If RTR is specified then the receiver indicates it is ready when
|
117 |
|
|
i_rtr_name is high. If RTRn is specified then the transmitter
|
118 |
|
|
indicates it is ready when i_rtr_name is low.
|
119 |
|
|
Note: The default, i.e., neither CTS nor CTSn is specified, is to always
|
120 |
|
|
enable the receiver.
|
121 |
|
|
Note: If there is no FIFO and the RTR/RTRn handshake indicates that the
|
122 |
|
|
receiver is not ready as soon as it starts receiving data and
|
123 |
|
|
until that data is read from the peripheral.
|
124 |
2 |
sinclairrf |
nStop=n
|
125 |
|
|
optionally configure the peripheral for n stop bits
|
126 |
|
|
default: 1 stop bit
|
127 |
|
|
Note: n must be 1 or 2
|
128 |
6 |
sinclairrf |
Note: the peripheral does not accept 1.5 stop bits\n
|
129 |
2 |
sinclairrf |
The following ports are provided by this peripheral:
|
130 |
|
|
I_inport_name
|
131 |
|
|
input a recieved byte from the peripheral
|
132 |
|
|
Note: If there is no input FIFO, then this is the last received byte.
|
133 |
|
|
If there is an input FIFO, then this is the next byte in the FIFO.
|
134 |
|
|
Note: If there is an input FIFO and the read would cause a FIFO
|
135 |
|
|
underflow, this will repeat the last received byte.
|
136 |
|
|
O_outport_name
|
137 |
|
|
output the next 8-bit value to transmit or to queue for transmission
|
138 |
|
|
Note: If there is no output FIFO or if there is an output FIFO and this
|
139 |
|
|
write would cause a FIFO overflow, then this byte will be
|
140 |
|
|
discarded.
|
141 |
|
|
I_inempty_name
|
142 |
|
|
input the empty status of the input side of the peripheral
|
143 |
|
|
bit 0: input empty
|
144 |
|
|
this bit will be high when the input side of the peripheral has one or
|
145 |
|
|
more bytes read to be read
|
146 |
|
|
Note: If there is no FIFO this means that a single byte is ready to be
|
147 |
|
|
read and has not been read. If there is an input FIFO this
|
148 |
|
|
means that there are one or more bytes in the FIFO.
|
149 |
|
|
Note: "Empty" is used rather than "ready" to facilitate loops that
|
150 |
|
|
respond when there is a new byte ready to be processed. See the
|
151 |
|
|
examples below.
|
152 |
|
|
I_outstatus_name
|
153 |
|
|
input the status of the output side of the peripheral
|
154 |
|
|
bit 0: output busy
|
155 |
|
|
this bit will be high when the output side of the peripheral cannot
|
156 |
|
|
accept more writes
|
157 |
|
|
Note: If there is no FIFO this means that the peripheral is still
|
158 |
|
|
transmitting the last byte. If there is an output FIFO it means
|
159 |
|
|
that it is full.\n
|
160 |
|
|
Note: "Busy" is used rather that "ready" to facilitate loops that wait
|
161 |
6 |
sinclairrf |
for a not-busy status to send the next byte. See the examples below.\n
|
162 |
2 |
sinclairrf |
WARNING: The peripheral is very simple and does not protect against writing a
|
163 |
|
|
new value in the middle of a transmition or writing to a full FIFO.
|
164 |
|
|
Adding such logic would be contrary to the design principle of
|
165 |
|
|
keeping the HDL small and relying on the assembly code to provide
|
166 |
|
|
the protection.\n
|
167 |
|
|
Example: Configure the UART for 115200 baud using a 100 MHz clock and
|
168 |
|
|
transmit the message "Hello World!"\n
|
169 |
|
|
Within the processor architecture file include the configuration command:\n
|
170 |
|
|
PERIPHERAL UART_Tx O_UART_TX I_UART_TX baudmethod=100_000_000/115200\n
|
171 |
|
|
Use the following assembly code to transmit the message "Hello World!".
|
172 |
|
|
This transmits the entire message whether or not the peripheral has a FIFO.\n
|
173 |
|
|
N"Hello World!\\r\\n"
|
174 |
|
|
:loop .outport(O_UART_TX) :wait .inport(I_UART_TX_BUSY) .jumpc(wait) .jumpc(loop,nop) drop
|
175 |
|
|
"""
|
176 |
|
|
|
177 |
|
|
def __init__(self,peripheralFile,config,param_list,loc):
|
178 |
|
|
# Use the externally provided file name for the peripheral
|
179 |
|
|
self.peripheralFile = peripheralFile;
|
180 |
|
|
# Get the parameters.
|
181 |
6 |
sinclairrf |
allowables = (
|
182 |
|
|
( 'CTS', r'i_\w+$', None, ),
|
183 |
|
|
( 'CTSn', r'i_\w+$', None, ),
|
184 |
|
|
( 'RTR', r'o_\w+$', None, ),
|
185 |
|
|
( 'RTRn', r'o_\w+$', None, ),
|
186 |
|
|
( 'baudmethod', r'\S+$', lambda v : self.RateMethod(config,v), ),
|
187 |
|
|
( 'deglitch', r'[1-9]\d*$', int, ),
|
188 |
|
|
( 'inFIFO', r'[1-9]\d*$', lambda v : self.IntPow2(v), ),
|
189 |
|
|
( 'inempty', r'I_\w+$', None, ),
|
190 |
|
|
( 'inport', r'I_\w+$', None, ),
|
191 |
|
|
( 'insignal', r'i_\w+$', None, ),
|
192 |
|
|
( 'noDeglitch', None, None, ),
|
193 |
|
|
( 'noInFIFO', None, None, ),
|
194 |
|
|
( 'noOutFIFO', None, None, ),
|
195 |
|
|
( 'noSync', None, None, ),
|
196 |
|
|
( 'nStop', r'[12]$', int, ),
|
197 |
|
|
( 'outFIFO', r'[1-9]\d*$', lambda v : self.IntPow2(v), ),
|
198 |
|
|
( 'outport', r'O_\w+$', None, ),
|
199 |
|
|
( 'outsignal', r'o_\w+$', None, ),
|
200 |
|
|
( 'outstatus', r'I_\w+$', None, ),
|
201 |
|
|
( 'sync', r'[1-9]\d*$', int, ),
|
202 |
|
|
);
|
203 |
|
|
names = [a[0] for a in allowables];
|
204 |
2 |
sinclairrf |
for param_tuple in param_list:
|
205 |
|
|
param = param_tuple[0];
|
206 |
6 |
sinclairrf |
if param not in names:
|
207 |
|
|
raise SSBCCException('Unrecognized parameter "%s" at %s' % (param,loc,));
|
208 |
|
|
param_test = allowables[names.index(param)];
|
209 |
|
|
self.AddAttr(config,param,param_tuple[1],param_test[1],loc,param_test[2]);
|
210 |
2 |
sinclairrf |
# Ensure the required parameters are provided.
|
211 |
|
|
for paramname in (
|
212 |
|
|
'baudmethod',
|
213 |
|
|
'inempty',
|
214 |
|
|
'inport',
|
215 |
|
|
'outport',
|
216 |
|
|
'outstatus',
|
217 |
|
|
):
|
218 |
|
|
if not hasattr(self,paramname):
|
219 |
|
|
raise SSBCCException('Required parameter "%s" is missing at %s' % (paramname,loc,));
|
220 |
|
|
# Set optional parameters.
|
221 |
|
|
for optionalpair in (
|
222 |
6 |
sinclairrf |
( 'insignal', 'i_UART_Rx', ),
|
223 |
|
|
( 'nStop', 1, ),
|
224 |
|
|
( 'outsignal', 'o_UART_Tx', ),
|
225 |
2 |
sinclairrf |
):
|
226 |
|
|
if not hasattr(self,optionalpair[0]):
|
227 |
|
|
setattr(self,optionalpair[0],optionalpair[1]);
|
228 |
|
|
# Ensure exclusive pair configurations are set and consistent.
|
229 |
|
|
for exclusivepair in (
|
230 |
6 |
sinclairrf |
( 'CTS', 'CTSn', None, None, ),
|
231 |
|
|
( 'RTR', 'RTRn', None, None, ),
|
232 |
|
|
( 'noSync', 'sync', 'sync', 3, ),
|
233 |
|
|
( 'noDeglitch', 'deglitch', 'noDeglitch', True, ),
|
234 |
|
|
( 'noInFIFO', 'inFIFO', 'noInFIFO', True, ),
|
235 |
|
|
( 'noOutFIFO', 'outFIFO', 'noOutFIFO', True, ),
|
236 |
2 |
sinclairrf |
):
|
237 |
|
|
if hasattr(self,exclusivepair[0]) and hasattr(self,exclusivepair[1]):
|
238 |
|
|
raise SSBCCException('Only one of "%s" and "%s" can be specified at %s' % (exclusivepair[0],exclusivepair[1],loc,));
|
239 |
6 |
sinclairrf |
if not hasattr(self,exclusivepair[0]) and not hasattr(self,exclusivepair[1]) and exclusivepair[2]:
|
240 |
2 |
sinclairrf |
setattr(self,exclusivepair[2],exclusivepair[3]);
|
241 |
6 |
sinclairrf |
# Convert configurations to alternative format.
|
242 |
|
|
for equivalent in (
|
243 |
|
|
( 'noDeglitch', 'deglitch', 0, ),
|
244 |
|
|
( 'noInFIFO', 'inFIFO', 0, ),
|
245 |
|
|
( 'noOutFIFO', 'outFIFO', 0, ),
|
246 |
|
|
( 'noSync', 'sync', 0, ),
|
247 |
|
|
):
|
248 |
|
|
if hasattr(self,equivalent[0]):
|
249 |
|
|
delattr(self,equivalent[0]);
|
250 |
|
|
setattr(self,equivalent[1],equivalent[2]);
|
251 |
2 |
sinclairrf |
# Set the string used to identify signals associated with this peripheral.
|
252 |
|
|
self.namestring = self.outsignal;
|
253 |
|
|
# Add the I/O port, internal signals, and the INPORT and OUTPORT symbols for this peripheral.
|
254 |
6 |
sinclairrf |
for ioEntry in (
|
255 |
|
|
( 'insignal', 1, 'input', ),
|
256 |
|
|
( 'outsignal', 1, 'output', ),
|
257 |
|
|
( 'CTS', 1, 'input', ),
|
258 |
|
|
( 'CTSn', 1, 'input', ),
|
259 |
|
|
( 'RTR', 1, 'output', ),
|
260 |
|
|
( 'RTRn', 1, 'output', ),
|
261 |
|
|
):
|
262 |
|
|
if hasattr(self,ioEntry[0]):
|
263 |
|
|
config.AddIO(getattr(self,ioEntry[0]),ioEntry[1],ioEntry[2],loc);
|
264 |
2 |
sinclairrf |
config.AddSignal('s__%s__Rx' % self.namestring,8,loc);
|
265 |
|
|
config.AddSignal('s__%s__Rx_empty' % self.namestring,1,loc);
|
266 |
|
|
config.AddSignal('s__%s__Rx_rd' % self.namestring,1,loc);
|
267 |
|
|
config.AddSignal('s__%s__Tx' % self.namestring,8,loc);
|
268 |
|
|
config.AddSignal('s__%s__Tx_busy' % self.namestring,1,loc);
|
269 |
|
|
config.AddSignal('s__%s__Tx_wr' % self.namestring,1,loc);
|
270 |
|
|
config.AddInport((self.inport,
|
271 |
|
|
('s__%s__Rx' % self.namestring,8,'data',),
|
272 |
|
|
('s__%s__Rx_rd' % self.namestring,1,'strobe',),
|
273 |
|
|
),loc);
|
274 |
|
|
config.AddInport((self.inempty,
|
275 |
|
|
('s__%s__Rx_empty' % self.namestring,1,'data',),
|
276 |
|
|
),loc);
|
277 |
|
|
config.AddOutport((self.outport,False,
|
278 |
|
|
('s__%s__Tx' % self.namestring,8,'data',),
|
279 |
|
|
('s__%s__Tx_wr' % self.namestring,1,'strobe',),
|
280 |
|
|
),loc);
|
281 |
|
|
config.AddInport((self.outstatus,
|
282 |
|
|
('s__%s__Tx_busy' % self.namestring,1,'data',),
|
283 |
|
|
),loc);
|
284 |
|
|
# Add the 'clog2' function to the processor (if required).
|
285 |
|
|
config.functions['clog2'] = True;
|
286 |
|
|
|
287 |
|
|
def GenVerilog(self,fp,config):
|
288 |
|
|
for bodyextension in ('_Rx.v','_Tx.v',):
|
289 |
|
|
body = self.LoadCore(self.peripheralFile,bodyextension);
|
290 |
6 |
sinclairrf |
if hasattr(self,'RTR') or hasattr(self,'RTRn'):
|
291 |
|
|
body = re.sub(r'@RTR_BEGIN@\n','',body);
|
292 |
|
|
body = re.sub(r'@RTR_END@\n','',body);
|
293 |
|
|
else:
|
294 |
|
|
if re.search(r'@RTR_BEGIN@',body):
|
295 |
|
|
body = re.sub(r'@RTR_BEGIN@.*?@RTR_END@\n','',body,flags=re.DOTALL);
|
296 |
2 |
sinclairrf |
for subpair in (
|
297 |
6 |
sinclairrf |
( r'@RTR_SIGNAL@', self.RTR if hasattr(self,'RTR') else self.RTRn if hasattr(self,'RTRn') else '', ),
|
298 |
|
|
( r'@RTR_INVERT@', '' if hasattr(self,'RTR') else '!', ),
|
299 |
|
|
( r'\bL__', 'L__@NAME@__', ),
|
300 |
|
|
( r'\bgen__', 'gen__@NAME@__', ),
|
301 |
|
|
( r'\bs__', 's__@NAME@__', ),
|
302 |
|
|
( r'@INPORT@', self.insignal, ),
|
303 |
|
|
( r'@BAUDMETHOD@', str(self.baudmethod), ),
|
304 |
|
|
( r'@SYNC@', str(self.sync), ),
|
305 |
|
|
( r'@DEGLITCH@', str(self.deglitch), ),
|
306 |
|
|
( r'@INFIFO@', str(self.inFIFO), ),
|
307 |
|
|
( r'@ENABLED@', self.CTS if hasattr(self,'CTS') else ('!%s' % self.CTSn) if hasattr(self,'CTSn') else '1\'b1', ),
|
308 |
|
|
( r'@NSTOP@', str(self.nStop), ),
|
309 |
|
|
( r'@OUTFIFO@', str(self.outFIFO), ),
|
310 |
|
|
( r'@NAME@', self.namestring, ),
|
311 |
|
|
):
|
312 |
|
|
if re.search(subpair[0],body):
|
313 |
|
|
body = re.sub(subpair[0],subpair[1],body);
|
314 |
2 |
sinclairrf |
body = self.GenVerilogFinal(config,body);
|
315 |
|
|
fp.write(body);
|