1 |
9 |
sinclairrf |
################################################################################
|
2 |
|
|
#
|
3 |
|
|
# Copyright 2015, Sinclair R.F., Inc.
|
4 |
|
|
#
|
5 |
|
|
################################################################################
|
6 |
|
|
|
7 |
|
|
import math
|
8 |
|
|
|
9 |
|
|
from ssbccPeripheral import SSBCCperipheral
|
10 |
|
|
from ssbccUtil import CeilLog2
|
11 |
|
|
from ssbccUtil import SSBCCException
|
12 |
|
|
|
13 |
|
|
class stepper_motor(SSBCCperipheral):
|
14 |
|
|
"""
|
15 |
|
|
Stepper motor driver\n
|
16 |
|
|
This peripheral creates pulses to driver a stepper motor driver such as TI's
|
17 |
|
|
DRV8825. It includes a buffer which can be used to store acceleration,
|
18 |
|
|
motion, and deceleration profiles and returns a "completed" status to the
|
19 |
|
|
micro controller.\n
|
20 |
|
|
The core runs accumulators for the angle and the rate. I.e. the rate is an
|
21 |
|
|
accumulated sum of the initial rate and the commanded acceleration and the
|
22 |
11 |
sinclairrf |
angle is an accumulation of this possibly changing rate. The direction
|
23 |
|
|
signal (if present) to the stepper motor is the sign bit from the accumulated
|
24 |
|
|
rate. The "step" signal to the stepper motor driver is strobed every time
|
25 |
|
|
the accumulated angle overflows or underflows and the direction bit to the
|
26 |
|
|
driver is set according to whether the accumulated angle overflowed or
|
27 |
|
|
underflowed.\n
|
28 |
9 |
sinclairrf |
The motor control word consists of the following signals:
|
29 |
|
|
initial rate
|
30 |
|
|
acceleration
|
31 |
|
|
number of steps to be performed this control word
|
32 |
|
|
optional mode specification
|
33 |
|
|
These control words are individually packed into one or more 8-bit bytes. The
|
34 |
|
|
8-bit values from the micro controller are shifted into a buffer from which
|
35 |
|
|
the control word is constructed. This completed control word is then shifted
|
36 |
|
|
into a FIFO. The control words in the FIFO are then used to generate the
|
37 |
|
|
timing for the step strobes to the stepper motor driver. When the FIFO
|
38 |
|
|
empties, the rate and the acceleration are set to zero and the mode retains
|
39 |
|
|
its most recent value. The user must ensure they do not overfill the FIFO --
|
40 |
|
|
the only FIFO status is an "empty" or control words "done" status.\n
|
41 |
|
|
Usage:
|
42 |
|
|
PERIPHERAL stepper_motor basename=name \\
|
43 |
|
|
outcontrol=O_name \\
|
44 |
|
|
outrecord=O_name \\
|
45 |
|
|
outrun=O_name \\
|
46 |
|
|
indone=I_name \\
|
47 |
11 |
sinclairrf |
[inerror=I_name] \\
|
48 |
|
|
[nodir] \\
|
49 |
9 |
sinclairrf |
ratemethod={CLK_FREQ_HZ/RATE_HZ|count} \\
|
50 |
|
|
ratescale=N_rate_scale \\
|
51 |
|
|
rateres=N_rate \\
|
52 |
|
|
accelscale=N_accel_scale \\
|
53 |
|
|
accelres=N_accel \\
|
54 |
|
|
[accumres=N_accum] \\
|
55 |
|
|
countwidth=N_count \\
|
56 |
|
|
[modewidth=N_mode] \\
|
57 |
|
|
[FIFO=N_fifo]\n
|
58 |
|
|
Or:\n
|
59 |
|
|
PERIPHERAL stepper_motor basename=name \\
|
60 |
|
|
master=mastername \\
|
61 |
|
|
outrecord=O_name \\
|
62 |
|
|
outrun=O_name \\
|
63 |
|
|
indone=I_name \\
|
64 |
11 |
sinclairrf |
[nodir] \\
|
65 |
|
|
[inerror=I_name] \\
|
66 |
9 |
sinclairrf |
[FIFO=N_fifo]\n
|
67 |
|
|
Where:
|
68 |
|
|
basename=name
|
69 |
|
|
specifies the name used to contruct the I/O signals
|
70 |
|
|
Note: The name must start with an alphabetic character.
|
71 |
|
|
Example: "basename=stepper" results in the names "o_stepper_dir",
|
72 |
|
|
"o_stepper_step", and "o_stepper_mode" for the output
|
73 |
|
|
direction, step, and optional mode signals and
|
74 |
|
|
"i_stepper_error" for the input error signal.
|
75 |
|
|
master=mastername
|
76 |
|
|
specifies a preceding stepper_motor peripheral to use for the internal
|
77 |
|
|
clock and to use for the accleration, rate, angle accumulator, and mode
|
78 |
|
|
sizes
|
79 |
10 |
sinclairrf |
Note: The "outcontrol" port from the master peripheral is used to queue
|
80 |
|
|
the control words for its slaves.
|
81 |
9 |
sinclairrf |
outcontrol=O_name
|
82 |
|
|
specifies the port used to assemble 8-bit control values into the stepper
|
83 |
|
|
motor control word
|
84 |
|
|
Note: The name must start with "O_".
|
85 |
|
|
outrecord=O_name
|
86 |
|
|
specifies the port used to generate the strobe that pushes the assembled
|
87 |
|
|
motor control word into the stepper motor FIFO
|
88 |
|
|
Note: The name must start with "O_".
|
89 |
|
|
outrun=O_name
|
90 |
|
|
specified the port used to begin the sequence of operations specified by
|
91 |
|
|
the motor control words in the buffer
|
92 |
|
|
Note: The name must start with "O_".
|
93 |
|
|
indone=I_name
|
94 |
|
|
specifies the port used to determine whether or not the operations in the
|
95 |
|
|
FIFO have finished
|
96 |
|
|
Note: The name must start with "I_".
|
97 |
|
|
inerror=I_name
|
98 |
11 |
sinclairrf |
optionally specifies the port used to read the error status from the
|
99 |
|
|
stepper motor controller
|
100 |
9 |
sinclairrf |
Note: The name must start with "I_".
|
101 |
11 |
sinclairrf |
nodir
|
102 |
|
|
optionally specify that the stepper motor does not generate a direction
|
103 |
|
|
bit
|
104 |
9 |
sinclairrf |
ratemethod
|
105 |
|
|
specified the method to generate the internal clock rate from the
|
106 |
|
|
processor clock
|
107 |
|
|
1st method: CLK_FREQ_HZ/RATE_HZ
|
108 |
|
|
CLK_FREQ_HZ is the frequency of "i_clk" in Hz
|
109 |
|
|
a number will be interpreted as the clock frequency in Hz
|
110 |
|
|
a symbol will be interpreted as a constant or a parameter
|
111 |
|
|
Note: the symbol must be declared with the CONSTANT, LOCALPARARM,
|
112 |
|
|
or PARAMETER configuration command.
|
113 |
|
|
RATE_HZ is the desired internal clock rate
|
114 |
|
|
this is specified as per "CLK_FREQ_HZ"
|
115 |
|
|
2nd method: count
|
116 |
|
|
specify the number of "i_clk" clock cycles per internal clock cycle
|
117 |
|
|
Note: CLK_FREQ_HZ, RATE_HZ, and count can be parameters or constants. For example,
|
118 |
|
|
the following uses the parameter C_CLK_FREQ_HZ for the clock
|
119 |
|
|
frequency and an internal rate to 500 kHz:
|
120 |
|
|
"ratemethod=C_CLK_FREQ_HZ/500_000".
|
121 |
|
|
Note: The minimum value of "ratemethod" is 2.
|
122 |
|
|
ratescale=N_rate_scale
|
123 |
|
|
specifies the scaling for the most significant bit of the rate
|
124 |
|
|
Note: See the 'a' parameter in the "Theory of Operation" section.
|
125 |
|
|
rateres=N_rate
|
126 |
|
|
specifies the resolution of the rate
|
127 |
|
|
Note: See the 'r' parameter in the "Theory of Operation" section.
|
128 |
|
|
accelscale=N_accel_scale
|
129 |
|
|
specifies the scaling for the most significant bit of the acceleration
|
130 |
|
|
Note: See the 'a' parameter in the "Theory of Operation" section.
|
131 |
|
|
accelres=N_accel
|
132 |
|
|
specifies the resolution for the acceleration
|
133 |
|
|
Note: See the 'b' parameter in the "Theory of Operation" section.
|
134 |
|
|
accumres=N_accum
|
135 |
|
|
optionally specify the resolution for the accumulator to the summed angle
|
136 |
|
|
(from which the step strobes are generated)
|
137 |
|
|
Note: This must be between rateres and accelres.
|
138 |
|
|
Note: The default value is accelres.
|
139 |
|
|
countwidth=N_count
|
140 |
|
|
specifies the width of the counter for the number of steps to be performed
|
141 |
|
|
by the control word
|
142 |
|
|
modewidth=N_mode
|
143 |
|
|
- if not specified, there is no mode signal to the stepper motor
|
144 |
|
|
controller
|
145 |
|
|
- if specified then this specifies the width of the signal to the stepper
|
146 |
|
|
motor controller
|
147 |
|
|
FIFO=N_fifo
|
148 |
|
|
optionally specify the depth of the control word FIFO
|
149 |
|
|
Note: This must be a power of 2 and must be at least 16.
|
150 |
|
|
Note: The default is 16.\n
|
151 |
|
|
Theory of Operation:
|
152 |
|
|
Define the following:
|
153 |
|
|
n is the number of internal clock cycles since the control word
|
154 |
|
|
started being performed (i.e., these are counted after the dlock
|
155 |
|
|
rate is reduced by ratemethod)
|
156 |
|
|
F is the internal clock cycle frequency (i.e., RATE_HZ in the
|
157 |
|
|
second form for specifying the ratemethod
|
158 |
|
|
R_0 is the initial rate command
|
159 |
|
|
R_n is the accumulated rate after n internal clock cycles
|
160 |
|
|
A is the commanded acceleration
|
161 |
|
|
S_n is the accumulated step after n internal clock cycles
|
162 |
|
|
Then
|
163 |
|
|
R_n = R_0 + A * n
|
164 |
|
|
S_n = R_0 * n + A * n * (n-1) / 2
|
165 |
|
|
The rate R_n can be thought of as a signed fraction with the format "s0.r"
|
166 |
|
|
where 's' represents the sign bit, there are no bits to the left of the
|
167 |
|
|
decimal, and there are 'r' bits to the right of the decimal. Then the rate
|
168 |
|
|
can be as high as F and as low as F/2^r. Practically, the maximum rate
|
169 |
|
|
cannot exceed half the internal clock frequency, otherwise the "step"
|
170 |
|
|
signals will merge together and the stepper driver will not see distinct
|
171 |
|
|
driver pulses.\n
|
172 |
|
|
Similarly, the acceleration command A can be thought of as a signed fraction
|
173 |
|
|
with the format "sa.b". Here 's' again represents the sign bit and 'b'
|
174 |
|
|
represents the number of bits to the right of the decimial, but 'a' is a
|
175 |
|
|
negative number representing the first bit in A. I.e., aside from the sign
|
176 |
|
|
bit, A is b+a+1 bits wide. For example, the specification s-4.8 means that
|
177 |
|
|
A has a sign bit with 8-4+1 = 5 bits for the value of A with the leasts
|
178 |
|
|
significant bit representing a rate of F^2/2^8.\n
|
179 |
|
|
The bit widths are determined as follows: Let mR be the minimum non-zero
|
180 |
|
|
magnitude of the rate, mA be the minimum non-zero mangitude of the
|
181 |
|
|
acceleration, and MA be the mamximum magnitude of the acceleration, all in
|
182 |
|
|
step/sec or step/sec^2. Then\n
|
183 |
|
|
r = ceil(-log_2(mR/F))\n
|
184 |
|
|
a = floor(log_2(MA/F^2))\n
|
185 |
|
|
b = ceil(-log_2(mA/f^2))\n
|
186 |
|
|
Note: r and b may be increased by a few bits if accurate representations of
|
187 |
|
|
the minimum rates are needed.\n
|
188 |
|
|
Example:
|
189 |
|
|
A micro controller with an 8 MHz clock is used to operate a DRV8825 driving
|
190 |
|
|
a stepper motor assembly. The stepper motor has 200 steps per revolution,
|
191 |
|
|
can be operated in full-step or a 16-step micro step mode, has a maximum
|
192 |
|
|
rotation rate of 10 Hz, and has a maximum acceleration of 4 Hz/sec (i.e.,
|
193 |
|
|
800 full-steps/sec^2). The motor is attached to a 400mm theaded rod with a
|
194 |
|
|
pitch of 4mm per revolution.\n
|
195 |
|
|
The 1.9usec minimum high and low widths of the DRV8825 and the 8 MHz
|
196 |
|
|
processor clock mean that the stepper motor controller can realistically be
|
197 |
|
|
run at 500kHz. The rate method to divide the micro controller clock to the
|
198 |
|
|
internal processing rate is specified by "ratemethod" in the PERIPHERAL
|
199 |
|
|
command.\n
|
200 |
|
|
The bit widths are determine by choosing:\n
|
201 |
|
|
MR = 10 rev/sec * 200 full-step/rev * 16 micro-step/full-step
|
202 |
|
|
= 32000 micro-step/sec
|
203 |
|
|
==> R = -ceil(log_2(MR/F))
|
204 |
|
|
= -ceil(log_2((32000 micro-step/sec)/500kHz))
|
205 |
|
|
= 3
|
206 |
|
|
mR = 10 step/sec
|
207 |
|
|
==> r = -floor(log_2(mR/F))
|
208 |
|
|
= -floor(log_2((10 step/sec)/500kHz)
|
209 |
|
|
= 16\n
|
210 |
|
|
MA = 10 rev/sec^2 = 10*16*200 step/sec^2
|
211 |
|
|
==> A = -ceil(log_2(MA/F^2))
|
212 |
|
|
= -ceil(log_2((32,000 step/sec^2)/500kHz^2))
|
213 |
|
|
= 22\n
|
214 |
|
|
mA = 20 step/sec^2 (in full step mode)
|
215 |
|
|
==> a = -floor(log_2(mA/F^2))
|
216 |
|
|
= 34\n
|
217 |
|
|
The values R=3 and r=16 along with the sign bit mean the rate would be
|
218 |
|
|
stored in a signed 14-bit value. The rate requires two 8-bit writes to the
|
219 |
|
|
control word.\n
|
220 |
|
|
The values A=22 and a=34 mean the acceleration would be stored in a signed
|
221 |
|
|
1+(34-22) = 13 bit value. The acceleration requires two 8-bit writes to the
|
222 |
|
|
control word.\n
|
223 |
|
|
The accumulator width is set to the same value as the acceleration
|
224 |
|
|
resolution. This avoid non-linear trunction errors and makes the motion
|
225 |
|
|
profile more predictable using simple integer arithmetic.\n
|
226 |
|
|
The number of full steps to move from one of the of rod to the other is
|
227 |
|
|
(400mm/(4mm/rev)*(200steps/rev)=20_000 steps. In the micro-stepmode there
|
228 |
|
|
are 16 micro steps per full step, so at most 320_000 micro steps can be
|
229 |
|
|
performed before the full length of the rod is traversed. I.e., a 19-bit
|
230 |
|
|
counter will suffice for the worst-case unidirection motion. This 19-bit
|
231 |
|
|
count requires 3 8-bit writes to the control word.\n
|
232 |
|
|
A "modewidth" of 1 is specifies so that the controller can be operated in
|
233 |
|
|
either full step or a single hard-wired micro-step mode. If all 3 of the
|
234 |
|
|
DRV8825 mode pins were connected, then "modewidth=3" would need to
|
235 |
|
|
specified.\n
|
236 |
|
|
The peripheral is then specified as follows:\n
|
237 |
|
|
CONSTANT C_RATE_SCALE 3
|
238 |
|
|
CONSTANT C_RATE_RES 16
|
239 |
|
|
CONSTANT C_ACCEL_SCALE 22
|
240 |
|
|
CONSTANT C_ACCEL_RES 34
|
241 |
|
|
CONSTANT C_ACCUM_RES 34
|
242 |
|
|
CONSTANT C_COUNT_WIDTH 19
|
243 |
|
|
PERIPHERAL stepper_motor basename=stepper \\
|
244 |
|
|
outcontrol=O_stepper_control \\
|
245 |
|
|
outrecord=O_stepper_wr \\
|
246 |
|
|
outrun=O_stepper_go \\
|
247 |
|
|
indone=I_stepper_done \\
|
248 |
|
|
inerror=I_stepper_error \\
|
249 |
|
|
ratemethod=8_000_000/500_000 \\
|
250 |
|
|
ratescale=C_RATE_SCALE \\
|
251 |
|
|
rateres=C_RATE_RES \\
|
252 |
|
|
accelscale=C_ACCEL_SCALE \\
|
253 |
|
|
accelres=C_ACCEL_RES \\
|
254 |
|
|
accumres=C_ACCUM_RES \\
|
255 |
|
|
countwidth=C_COUNT_WIDTH \\
|
256 |
|
|
modewidth=1\n
|
257 |
|
|
and the TBD byte control words are pushed into the peripheral as follows:
|
258 |
|
|
R_0 14-bit initial rate stored in a 16-bit field (MSB first)
|
259 |
|
|
A 13-bit acceleration stored in a 16-bit field (MSB first)
|
260 |
|
|
COUNT 19-bit count stored in a 24-bit field (MSB first)
|
261 |
|
|
MODE 1-bit mode stored as the lsb of an 8-bit field
|
262 |
|
|
The control word is a total of 8 bytes wide.\n
|
263 |
|
|
To command the peripheral to accelerate from stop to 200 steps/sec in one
|
264 |
|
|
second in the forward direction using the full-step mode, the following
|
265 |
|
|
seqeuence of bytes would be written to the control port:
|
266 |
|
|
0x00 0x00 ; initial rate is zero
|
267 |
|
|
0x00 0x0E ; 200 step/sec^2 * 2^34 / 500kHz^2 = 14
|
268 |
|
|
0x00 0x00 0x63 ; send 100 step commands (command 100-1=99)
|
269 |
|
|
0x00 ; full-step mode
|
270 |
|
|
Note: It will take t=sqrt(2*100*2^34/14)/F = 0.99 sec to move the commanded
|
271 |
|
|
100 steps. At this time the speed will be r=t*14/2^34*F^2 = 201 step/sec.
|
272 |
|
|
A more accurate match to the commanded speed could be accomplished by adding
|
273 |
|
|
additional bits to the acceleration resolution at the cost of using more
|
274 |
|
|
FPGA resources. Alternatively, the acceleration could be commanded for 99
|
275 |
|
|
steps and any subsequent 200 step/sec motion could be lengthened by 1 step.
|
276 |
|
|
Another alternative would be to use a micro-step acceleration. Practically,
|
277 |
|
|
the computed command is within 0.5% of the desired step rate.\n
|
278 |
|
|
To command the peripheral to decelerate from 200 step/sec to zero in one
|
279 |
|
|
second, the following sequence of bytes would be written to the control
|
280 |
|
|
port:
|
281 |
|
|
0x00 0x01 0xDB ; 200 step/sec * 2^23 / 500kHz
|
282 |
|
|
0xFF 0x9C ; negative of the above acceleration
|
283 |
|
|
0x00 0x00 0x63 ; send 100 step commands (command 100-1=99)
|
284 |
|
|
0x00 ; full-step mode\n
|
285 |
|
|
The first of these two control words could be assembled and transmitted to
|
286 |
|
|
the peripheral as follows:\n
|
287 |
|
|
0x00 ; mode
|
288 |
|
|
.push24(${100-1}) ; send 100 step commands
|
289 |
|
|
.push16(14) ; 200 step/sec^2
|
290 |
|
|
.push16(0) ; initial rate is zero
|
291 |
|
|
${8-1} :loop swap .outport(O_stepper_control) .jumpc(loop,1-) drop
|
292 |
|
|
.outstrobe(O_stepper_wr) ; push the assembed control word into the FIFO
|
293 |
|
|
...
|
294 |
|
|
.outstrobe(O_stepper_go) ; perform the queued control words\n
|
295 |
|
|
Example:
|
296 |
|
|
Slave a second stepper motor controller peripheral to the preceding
|
297 |
|
|
periperal.\n
|
298 |
|
|
PERIPHERAL stepper_motor basename=slave \\
|
299 |
|
|
master=stepper \\
|
300 |
|
|
outrecord=O_slave_wr \\
|
301 |
|
|
outrun=O_slave_go \\
|
302 |
|
|
indone=I_slave_done \\
|
303 |
|
|
inerror=I_slave_error\n
|
304 |
|
|
This controller will use the internal clock generated by the first
|
305 |
|
|
controller and the scales, resolutions, and accumulator width will be the
|
306 |
|
|
same as that master peripheral. What will be different is the four I/O
|
307 |
|
|
ports used to operate and status the controller.
|
308 |
|
|
"""
|
309 |
|
|
|
310 |
|
|
def __init__(self,peripheralFile,config,param_list,loc):
|
311 |
|
|
# Use the externally provided file name for the peripheral
|
312 |
|
|
self.peripheralFile = peripheralFile
|
313 |
|
|
# Get the parameters.
|
314 |
|
|
allowables = (
|
315 |
11 |
sinclairrf |
( 'FIFO', r'\S+$', lambda v : self.IntPow2Method(config,v,lowLimit=16), ),
|
316 |
|
|
( 'accelres', r'\S+$', lambda v : self.IntMethod(config,v,lowLimit=1), ),
|
317 |
|
|
( 'accelscale', r'\S+$', lambda v : self.IntMethod(config,v,lowLimit=1), ),
|
318 |
|
|
( 'accumres', r'\S+$', lambda v : self.IntMethod(config,v,lowLimit=1), ),
|
319 |
|
|
( 'basename', r'[A-Za-z]\w*$', None, ),
|
320 |
|
|
( 'countwidth', r'\S+$', lambda v : self.IntMethod(config,v,lowLimit=1), ),
|
321 |
|
|
( 'indone', r'I_\w+$', None, ),
|
322 |
|
|
( 'inerror', r'I_\w+$', None, ),
|
323 |
|
|
( 'master', r'[A-Za-z]\w*$', None, ),
|
324 |
|
|
( 'modewidth', r'\S+$', lambda v : self.IntMethod(config,v,lowLimit=1), ),
|
325 |
|
|
( 'nodir', None, None, ),
|
326 |
|
|
( 'outcontrol', r'O_\w+$', None, ),
|
327 |
|
|
( 'outrecord', r'O_\w+$', None, ),
|
328 |
|
|
( 'outrun', r'O_\w+$', None, ),
|
329 |
|
|
( 'ratemethod', r'\S+$', lambda v : self.RateMethod(config,v), ),
|
330 |
|
|
( 'rateres', r'\S+$', lambda v : self.IntMethod(config,v,lowLimit=1), ),
|
331 |
|
|
( 'ratescale', r'\S+$', lambda v : self.IntMethod(config,v,lowLimit=1), ),
|
332 |
9 |
sinclairrf |
)
|
333 |
|
|
names = [a[0] for a in allowables]
|
334 |
|
|
for param_tuple in param_list:
|
335 |
|
|
param = param_tuple[0]
|
336 |
|
|
if param not in names:
|
337 |
|
|
raise SSBCCException('Unrecognized parameter "%s" at %s' % (param,loc,))
|
338 |
|
|
param_test = allowables[names.index(param)]
|
339 |
|
|
self.AddAttr(config,param,param_tuple[1],param_test[1],loc,param_test[2])
|
340 |
|
|
# Signals that can't be specified when a master is specified.
|
341 |
|
|
masterExclude = (
|
342 |
|
|
'accelres',
|
343 |
|
|
'accelscale',
|
344 |
|
|
'accumres',
|
345 |
|
|
'countwidth',
|
346 |
|
|
'modewidth',
|
347 |
10 |
sinclairrf |
'outcontrol',
|
348 |
9 |
sinclairrf |
'ratemethod',
|
349 |
|
|
'rateres',
|
350 |
|
|
'ratescale',
|
351 |
|
|
)
|
352 |
|
|
# Ensure the required parameters are provided.
|
353 |
|
|
reqdParms = (
|
354 |
|
|
'basename',
|
355 |
|
|
'indone',
|
356 |
|
|
'outrecord',
|
357 |
|
|
'outrun',
|
358 |
|
|
)
|
359 |
|
|
if not hasattr(self,'master'):
|
360 |
|
|
reqdParms += tuple([me for me in masterExclude if me not in ('accumres','modewidth',)])
|
361 |
|
|
for paramname in reqdParms:
|
362 |
|
|
if not hasattr(self,paramname):
|
363 |
|
|
raise SSBCCException('Required parameter "%s" is missing at %s' % (paramname,loc,))
|
364 |
|
|
# Ensure mutually exclusive parameters are not listed.
|
365 |
|
|
if hasattr(self,'master'):
|
366 |
|
|
for paramname in masterExclude:
|
367 |
|
|
if hasattr(self,paramname):
|
368 |
|
|
raise SSBCCException('Parameter "%s" cannot be specified alongside "master" at %s' % (paramname,loc,))
|
369 |
|
|
# Ensure basename is unique for this class of peripheral
|
370 |
|
|
for p in config.peripheral:
|
371 |
|
|
if (str(p.__class__) == str(self.__class__)) and (p.basename == self.basename):
|
372 |
|
|
raise SSBCCException('Duplicated stepper_motor basename "%s" at %s' % (self.basename,loc,))
|
373 |
|
|
# For slaves, copy the bit widths from the master peripheral.
|
374 |
|
|
if hasattr(self,'master'):
|
375 |
|
|
for p in config.peripheral:
|
376 |
|
|
if (str(p.__class__) == str(self.__class__)) and (p.basename == self.master):
|
377 |
|
|
break
|
378 |
|
|
else:
|
379 |
|
|
raise SSBCCException('Can\'t find preceding stepper_motor peripheral with basename=%s at %s ' % (self.master,loc,))
|
380 |
|
|
self.master = p
|
381 |
|
|
for paramname in masterExclude:
|
382 |
|
|
setattr(self,paramname,getattr(self.master,paramname))
|
383 |
|
|
# Set unspecified optional parameters.
|
384 |
|
|
if not hasattr(self,'accumres'):
|
385 |
|
|
self.accumres = self.accelres
|
386 |
|
|
if not hasattr(self,'modewidth'):
|
387 |
|
|
self.modewidth = 0
|
388 |
|
|
if not hasattr(self,'FIFO'):
|
389 |
|
|
self.FIFO = 16
|
390 |
|
|
# Ensure the parameters satisfy any mutual constraints.
|
391 |
|
|
if not (self.rateres < self.accelres):
|
392 |
|
|
raise SSBCCException('rateres should be smaller than accelres at %s' % loc)
|
393 |
|
|
if not (self.rateres <= self.accumres <= self.accelres):
|
394 |
|
|
raise SSBCCException('accumres must be between rateres and accelres at %s' % loc)
|
395 |
|
|
# Add the I/O port, internal signals, and the INPORT and OUTPORT symbols for this peripheral.
|
396 |
11 |
sinclairrf |
if not hasattr(self,'nodir'):
|
397 |
|
|
config.AddIO('o_%s_dir' % self.basename, 1, 'output', loc)
|
398 |
|
|
config.AddIO('o_%s_step' % self.basename, 1, 'output', loc)
|
399 |
9 |
sinclairrf |
if self.modewidth > 0:
|
400 |
11 |
sinclairrf |
config.AddIO('o_%s_mode' % self.basename, 1, 'output', loc)
|
401 |
|
|
if hasattr(self,'inerror'):
|
402 |
|
|
config.AddIO('i_%s_error' % self.basename, 1, 'input', loc)
|
403 |
9 |
sinclairrf |
config.AddSignal('s__%s__done' % self.basename, 1, loc)
|
404 |
|
|
self.ix_outcontrol = config.NOutports()
|
405 |
10 |
sinclairrf |
if not hasattr(self,'master'):
|
406 |
|
|
config.AddOutport((self.outcontrol,
|
407 |
|
|
False,
|
408 |
|
|
# empty list
|
409 |
|
|
),loc)
|
410 |
9 |
sinclairrf |
self.ix_outrecord = config.NOutports()
|
411 |
|
|
config.AddOutport((self.outrecord,
|
412 |
|
|
True,
|
413 |
|
|
# empty list
|
414 |
|
|
),loc)
|
415 |
|
|
self.ix_outrun = config.NOutports()
|
416 |
|
|
config.AddOutport((self.outrun,
|
417 |
|
|
True,
|
418 |
|
|
# empty list
|
419 |
|
|
),loc)
|
420 |
11 |
sinclairrf |
if hasattr(self,'inerror'):
|
421 |
|
|
config.AddInport((self.inerror,
|
422 |
9 |
sinclairrf |
('i_%s_error' % self.basename, 1, 'data', ),
|
423 |
|
|
), loc)
|
424 |
|
|
config.AddInport((self.indone,
|
425 |
|
|
('s__%s__done' % self.basename, 1, 'data', ),
|
426 |
|
|
), loc)
|
427 |
|
|
# Compute bit widths.
|
428 |
|
|
dw = config.Get('data_width')
|
429 |
|
|
self.data_width = dw
|
430 |
|
|
self.ratecmdwidth = 1 + self.rateres - self.ratescale
|
431 |
|
|
self.ratewidth = 1 + self.accelres - self.ratescale
|
432 |
|
|
self.accelwidth = 1 + self.accelres - self.accelscale
|
433 |
|
|
self.accumwidth = self.accumres + 1
|
434 |
|
|
self.controlwidth = self.ratecmdwidth
|
435 |
|
|
self.controlwidth += dw*int((self.accelwidth+dw-1)/dw)
|
436 |
|
|
self.controlwidth += dw*int((self.countwidth+dw-1)/dw)
|
437 |
|
|
self.controlwidth += dw*int((self.modewidth+dw-1)/dw)
|
438 |
|
|
self.controlwidthpacked = self.ratecmdwidth + self.accelwidth + self.countwidth + self.modewidth
|
439 |
|
|
# Add the 'clog2' function to the processor (if required).
|
440 |
|
|
config.functions['clog2'] = True
|
441 |
|
|
|
442 |
|
|
def GenVerilog(self,fp,config):
|
443 |
|
|
body = self.LoadCore(self.peripheralFile,'.v')
|
444 |
|
|
if hasattr(self,'master'):
|
445 |
|
|
body = re.sub(r'@MASTER_BEGIN@.*?@MASTER_END@\n','',body,flags=re.DOTALL)
|
446 |
|
|
else:
|
447 |
|
|
body = re.sub(r'@MASTER_BEGIN@\n','',body)
|
448 |
|
|
body = re.sub(r'@MASTER_END@\n','',body)
|
449 |
|
|
if self.modewidth == 0:
|
450 |
|
|
body = re.sub(r'@OUTMODE_BEGIN@.*?@OUTMODE_END@\n','',body,flags=re.DOTALL)
|
451 |
|
|
else:
|
452 |
|
|
body = re.sub(r'@OUTMODE_BEGIN@\n','',body)
|
453 |
|
|
body = re.sub(r'@OUTMODE_END@\n','',body)
|
454 |
11 |
sinclairrf |
if hasattr(self,'nodir'):
|
455 |
|
|
body = re.sub(r' *o__dir.*?\n','',body)
|
456 |
10 |
sinclairrf |
masterBasename = self.basename if not hasattr(self,'master') else self.master.basename
|
457 |
9 |
sinclairrf |
for subpair in (
|
458 |
|
|
( r'@ACCEL_WIDTH@', str(self.accelwidth), ),
|
459 |
|
|
( r'@ACCEL_RES@', str(self.accelres), ),
|
460 |
|
|
( r'@ACCEL_SCALE@', str(self.accelscale), ),
|
461 |
|
|
( r'@ACCUM_RES@', str(self.accumres), ),
|
462 |
|
|
( r'@ACCUM_WIDTH@', str(self.accumwidth), ),
|
463 |
|
|
( r'@CONTROL_WIDTH@', str(self.controlwidth), ),
|
464 |
|
|
( r'@CONTROL_WIDTH_PACKED@', str(self.controlwidthpacked), ),
|
465 |
|
|
( r'@COUNT_WIDTH@', str(self.countwidth), ),
|
466 |
|
|
( r'@DW@', str(self.data_width), ),
|
467 |
|
|
( r'@DWM1@', str(self.data_width-1), ),
|
468 |
|
|
( r'@FIFO_DEPTH@', str(self.FIFO), ),
|
469 |
|
|
( r'@IX_OUTCONTROL@', str(self.ix_outcontrol), ),
|
470 |
|
|
( r'@IX_OUTRECORD@', str(self.ix_outrecord), ),
|
471 |
|
|
( r'@IX_OUTRUN@', str(self.ix_outrun), ),
|
472 |
|
|
( r'@MODE_WIDTH@', str(self.modewidth), ),
|
473 |
|
|
( r'@NAME@', self.basename, ),
|
474 |
|
|
( r'@NBITS_FIFO_DEPTH@', str(CeilLog2(self.FIFO)), ),
|
475 |
|
|
( r'@OUTMODEWIDTH@', str(self.modewidth), ),
|
476 |
|
|
( r'@RATECMD_WIDTH@', str(self.ratecmdwidth), ),
|
477 |
|
|
( r'@RATEMETHOD@', str(self.ratemethod), ),
|
478 |
|
|
( r'@RATE_RES@', str(self.rateres), ),
|
479 |
|
|
( r'@RATE_SCALE@', str(self.ratescale), ),
|
480 |
|
|
( r'@RATE_WIDTH@', str(self.ratewidth), ),
|
481 |
|
|
( r'\bL__', 'L__%s__' % self.basename, ),
|
482 |
|
|
( r'\bi__', 'i_%s_' % self.basename, ),
|
483 |
|
|
( r'\bo__', 'o_%s_' % self.basename, ),
|
484 |
|
|
( r'\bs__', 's__%s__' % self.basename, ),
|
485 |
10 |
sinclairrf |
( r'@S__CLK_EN@', 's__%s__clk_en' % masterBasename, ),
|
486 |
|
|
( r'@S__INPUT_CONTROL_WORD_PACKED@', 's__%s__input_control_word_packed' % masterBasename, ),
|
487 |
9 |
sinclairrf |
):
|
488 |
|
|
body = re.sub(subpair[0],subpair[1],body)
|
489 |
|
|
body = self.GenVerilogFinal(config,body)
|
490 |
|
|
fp.write(body)
|