1 |
2 |
sinclairrf |
################################################################################
|
2 |
|
|
#
|
3 |
|
|
# Copyright 2012-2013, Sinclair R.F., Inc.
|
4 |
|
|
#
|
5 |
|
|
################################################################################
|
6 |
|
|
|
7 |
|
|
from ssbccPeripheral import SSBCCperipheral
|
8 |
|
|
from ssbccUtil import SSBCCException;
|
9 |
|
|
|
10 |
|
|
class PWM_8bit(SSBCCperipheral):
|
11 |
|
|
"""
|
12 |
|
|
Pulse Width Modulator (PWM) with 8-bit control.\n
|
13 |
|
|
This peripheral creates one or more PWMs. The PWM is designed so that it is
|
14 |
|
|
allways off when the control is 0 and it is always on when the control is
|
15 |
|
|
0xFF.\n
|
16 |
|
|
Usage:
|
17 |
|
|
PERIPHERAL PWM_8bit outport=O_name \\
|
18 |
|
|
outsignal=o_name \\
|
19 |
|
|
ratemethod={clk/rate|count} \\
|
20 |
|
|
[invert|noinvert] \\
|
21 |
|
|
[instances=n] \\
|
22 |
|
|
[norunt]\n
|
23 |
|
|
Where:
|
24 |
|
|
outport=O_name
|
25 |
|
|
specifies the symbol used by the outport instruction to write a byte to
|
26 |
|
|
the peripheral
|
27 |
|
|
Note: The name must start with "O_".
|
28 |
|
|
outsignal=o_name
|
29 |
|
|
specifies the name of the output signal
|
30 |
|
|
Note: The name must start with "o_".
|
31 |
|
|
ratemethod={clk/rate|count}
|
32 |
|
|
specifies the frequency at which the PWM counter is incremented
|
33 |
|
|
Example: ratemethod=count means to increment the PWM counter once every
|
34 |
|
|
"count" clock cycles.
|
35 |
|
|
invert|noinvert
|
36 |
|
|
optional configuration command to invert or to not invert the PWM output
|
37 |
|
|
Default: don't invert the output (i.e., a command of 0 means the output is
|
38 |
|
|
always low)
|
39 |
|
|
Note: "invert" should be used when pulling the external signal to ground
|
40 |
|
|
means the device is "on"
|
41 |
|
|
instances=n
|
42 |
|
|
specifies the number of PWMs for the peripheral
|
43 |
|
|
Default: The default is one PWM control and output.
|
44 |
|
|
norunt
|
45 |
|
|
optionally add logic to ensure "runt" pulses are not generated by
|
46 |
|
|
incorporating new PWM commands at the start of the counting cycle
|
47 |
|
|
Default: "runt" pulses are allowed.\n
|
48 |
|
|
The following OUTPORT is provided by this peripheral when instances=1:
|
49 |
|
|
O_name
|
50 |
|
|
output the next 8-bit value to transmit or to queue for transmission\n
|
51 |
|
|
The following OUTPORT is provided by this peripheral when instances=n is larger
|
52 |
|
|
than 1:
|
53 |
|
|
O_name_0, O_name_1, ..., O_name_{n-1}
|
54 |
|
|
output the next 8-bit value to transmit on the specified PWM
|
55 |
|
|
Note: O_name_i = ${O_name_0+i) where 0<=i<n.
|
56 |
|
|
Note: The PWM for o_name[i] is controlled by the outport O_name_i
|
57 |
|
|
Example: If "instances=3" is specified, then the following outports are
|
58 |
|
|
provided: O_name_0, O_name_1, and O_name_2. The assembly
|
59 |
|
|
sequence "5 .outport(O_name_1)" will change the PWM control for
|
60 |
|
|
the second of these three PWMs to 5.\n
|
61 |
|
|
Note: The PWM counter is an 8-bit count that ranges from 1 to 255. Each PWM
|
62 |
|
|
output is '1' when this count is less than or equal to the commanded
|
63 |
|
|
count. The signal for a commanded count of 0 will never be on while
|
64 |
|
|
the signal for a commanded count of 255 will always be on.\n
|
65 |
|
|
Example: Control the intensity of an LED through a PWM. The LED must flicker
|
66 |
|
|
at a frequency greater than about 30 Hz in order for the flickering
|
67 |
|
|
to not be visible by human eyes. The LED is turned on when the
|
68 |
|
|
signal to the LED is at ground. The processor clock frequency is
|
69 |
|
|
provided by the parameter G_CLK_FREQ_HZ.\n
|
70 |
|
|
Within the processor architecture file include the configuration command:\n
|
71 |
|
|
PERIPHERAL PWM_8bit outport=O_PWM_LED \\
|
72 |
|
|
outsignal=o_led \\
|
73 |
|
|
ratemethod=G_CLK_FREQ_HZ/(30*255) \\
|
74 |
|
|
invert\n
|
75 |
|
|
Use the following assembly to set the LED to about 1/4 intensity:\n
|
76 |
|
|
0x40 .outport(O_PWM_LED)\n
|
77 |
|
|
Example: Similarly to obove, but for the three controls of a tri-color LED:\n
|
78 |
|
|
Within the processor architecture file include the configuration command:\n
|
79 |
|
|
PERIPHERAL PWM_8bit outport=O_PWM_LED \\
|
80 |
|
|
outsignal=o_led \\
|
81 |
|
|
ratemethod=G_CLK_FREQ_HZ/(30*255) \\
|
82 |
|
|
invert \\
|
83 |
|
|
instances=3\n
|
84 |
|
|
Use the following assembly to set the LED intensities to 0x10 0x20 and 0x55:\n
|
85 |
|
|
0x10 .outport(O_PWM_LED_0)
|
86 |
|
|
0x20 .outport(O_PWM_LED_1)
|
87 |
|
|
0x55 .outport(O_PWM_LED_2)\n
|
88 |
|
|
or use the following function to send the three values on the stack where
|
89 |
|
|
the top of the stack is 0x55 0x20 0x10 (this isn't less code, but it
|
90 |
|
|
illustrates how to increment the outport index):\n
|
91 |
|
|
; ( u_pwm_led_2 u_pwm_led_1 u_pwm_led_0 - )
|
92 |
|
|
.function set_pwm_led
|
93 |
|
|
O_PWM_LED_0 ${3-1} :loop r> swap over outport drop 1+ r> .jumpc(loop,1-) drop
|
94 |
|
|
.return(drop)
|
95 |
|
|
"""
|
96 |
|
|
|
97 |
|
|
def __init__(self,peripheralFile,config,param_list,loc):
|
98 |
|
|
# Use the externally provided file name for the peripheral
|
99 |
|
|
self.peripheralFile = peripheralFile;
|
100 |
|
|
# Get the parameters.
|
101 |
|
|
for param_tuple in param_list:
|
102 |
|
|
param = param_tuple[0];
|
103 |
|
|
param_arg = param_tuple[1];
|
104 |
|
|
if param == 'outport':
|
105 |
|
|
self.AddAttr(config,param,param_arg,r'O_\w+$',loc);
|
106 |
|
|
elif param == 'outsignal':
|
107 |
|
|
self.AddAttr(config,param,param_arg,r'o_\w+$',loc);
|
108 |
|
|
elif param == 'ratemethod':
|
109 |
|
|
self.ProcessRateMethod(config,param_arg,loc);
|
110 |
|
|
elif param == 'invert':
|
111 |
|
|
self.AddAttr(config,param,param_arg,None,loc);
|
112 |
|
|
elif param == 'noinvert':
|
113 |
|
|
self.AddAttr(config,param,param_arg,None,loc);
|
114 |
|
|
elif param == 'instances':
|
115 |
|
|
self.AddAttr(config,param,param_arg,r'[1-9]\d*$',loc,int);
|
116 |
|
|
elif param == 'norunt':
|
117 |
|
|
self.AddAttr(config,param,param_arg,None,loc);
|
118 |
|
|
else:
|
119 |
|
|
raise SSBCCException('Unrecognized parameter at %s: %s' % (loc,param,));
|
120 |
|
|
# Ensure the required parameters are provided.
|
121 |
|
|
if not hasattr(self,'instances'):
|
122 |
|
|
self.instances = 1;
|
123 |
|
|
# Set optional parameters.
|
124 |
|
|
if not hasattr(self,'invert') and not hasattr(self,'noinvert'):
|
125 |
|
|
self.noinvert = True;
|
126 |
|
|
if not hasattr(self,'norunt'):
|
127 |
|
|
self.norunt = False;
|
128 |
|
|
# Ensure parameters do not conflict.
|
129 |
|
|
if hasattr(self,'invert') and hasattr(self,'noinvert'):
|
130 |
|
|
raise SSBCCException('Only one of "invert" or "noinvert" can be specified at %s' % loc);
|
131 |
|
|
# Use only one of mutually exclusive configuration settings.
|
132 |
|
|
if hasattr(self,'noinvert'):
|
133 |
|
|
self.invert = False;
|
134 |
|
|
# Add the I/O port, internal signals, and the INPORT and OUTPORT symbols for this peripheral.
|
135 |
|
|
config.AddIO(self.outsignal,self.instances,'output',loc);
|
136 |
|
|
self.ix_outport_0 = config.NOutports();
|
137 |
|
|
if self.instances == 1:
|
138 |
|
|
tmpOutport = self.outport;
|
139 |
|
|
config.AddOutport((tmpOutport,False,),loc);
|
140 |
|
|
else:
|
141 |
|
|
for ixOutPort in range(self.instances):
|
142 |
|
|
tmpOutport = '%s_%d' % (self.outport,ixOutPort,);
|
143 |
|
|
config.AddOutport((tmpOutport,False,),loc);
|
144 |
|
|
# Add the 'clog2' function to the processor (if required).
|
145 |
|
|
config.functions['clog2'] = True;
|
146 |
|
|
|
147 |
|
|
def ProcessRateMethod(self,config,param_arg,loc):
|
148 |
|
|
if hasattr(self,'ratemethod'):
|
149 |
|
|
raise SSBCCException('ratemethod repeated at %s' % loc);
|
150 |
|
|
if param_arg.find('/') < 0:
|
151 |
|
|
if self.IsIntExpr(param_arg):
|
152 |
|
|
self.ratemethod = str(self.ParseIntExpr(param_arg));
|
153 |
|
|
elif self.IsParameter(config,param_arg):
|
154 |
|
|
self.ratemethod = param_arg;
|
155 |
|
|
else:
|
156 |
|
|
raise SSBCCException('ratemethod with no "/" must be an integer or a previously declared parameter at %s' % loc);
|
157 |
|
|
else:
|
158 |
|
|
baudarg = re.findall('([^/]+)',param_arg);
|
159 |
|
|
if len(baudarg) == 2:
|
160 |
|
|
if not self.IsIntExpr(baudarg[0]) and not self.IsParameter(config,baudarg[0]):
|
161 |
|
|
raise SSBCCException('Numerator in ratemethod must be an integer or a previously declared parameter at %s' % loc);
|
162 |
|
|
if not self.IsIntExpr(baudarg[1]) and not self.IsParameter(config,baudarg[1]):
|
163 |
|
|
raise SSBCCException('Denominator in ratemethod must be an integer or a previously declared parameter at %s' % loc);
|
164 |
|
|
for ix in range(2):
|
165 |
|
|
if self.IsIntExpr(baudarg[ix]):
|
166 |
|
|
baudarg[ix] = str(self.ParseIntExpr(baudarg[ix]));
|
167 |
|
|
self.ratemethod = '('+baudarg[0]+'+'+baudarg[1]+'/2)/'+baudarg[1];
|
168 |
|
|
if not hasattr(self,'ratemethod'):
|
169 |
|
|
raise SSBCCException('Bad ratemethod value at %s: "%s"' % (loc,param_arg,));
|
170 |
|
|
|
171 |
|
|
def GenVerilog(self,fp,config):
|
172 |
|
|
body = self.LoadCore(self.peripheralFile,'.v');
|
173 |
|
|
output_on = "1'b1";
|
174 |
|
|
output_off = "1'b0";
|
175 |
|
|
if self.invert:
|
176 |
|
|
output_on = "1'b0";
|
177 |
|
|
output_off = "1'b1";
|
178 |
|
|
norunt = "1'b0";
|
179 |
|
|
if self.norunt:
|
180 |
|
|
norunt = "1'b1";
|
181 |
|
|
for subs in (
|
182 |
|
|
(r'\bL__', 'L__@NAME@__',),
|
183 |
|
|
(r'\bgen__', 'gen__@NAME@__',),
|
184 |
|
|
(r'\bs__', 's__@NAME@__',),
|
185 |
|
|
(r'\bix\b', 'ix__@NAME@',),
|
186 |
|
|
(r'@COUNT@', self.ratemethod,),
|
187 |
|
|
(r'@INSTANCES@', str(self.instances),),
|
188 |
|
|
(r'@IX_OUTPORT_0@', str(self.ix_outport_0),),
|
189 |
|
|
(r'@OFF@', output_off,),
|
190 |
|
|
(r'@ON@', output_on,),
|
191 |
|
|
(r'@NAME@', self.outsignal,),
|
192 |
|
|
(r'@NORUNT@', norunt,),
|
193 |
|
|
):
|
194 |
|
|
body = re.sub(subs[0],subs[1],body);
|
195 |
|
|
body = self.GenVerilogFinal(config,body);
|
196 |
|
|
fp.write(body);
|