| 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);
|