1 |
12 |
sinclairrf |
################################################################################
|
2 |
|
|
#
|
3 |
|
|
# Copyright 2015, Sinclair R.F., Inc.
|
4 |
|
|
#
|
5 |
|
|
################################################################################
|
6 |
|
|
|
7 |
|
|
from ssbccPeripheral import SSBCCinterruptPeripheral
|
8 |
|
|
from ssbccUtil import SSBCCException
|
9 |
|
|
|
10 |
|
|
class interrupt(SSBCCinterruptPeripheral):
|
11 |
|
|
"""
|
12 |
|
|
Interrupt peripheral for 1 to 8 interrupt inputs.\n
|
13 |
|
|
This implements a variable-width interrupt input of up to 8 bits and
|
14 |
|
|
generates an interrupt when one of these goes from low to high, provided that
|
15 |
|
|
an interrupt is not already in progress.\n
|
16 |
|
|
Usage:
|
17 |
|
|
PERIPHERAL interrupt insignal<N>=[!]{i_name|s__name}[,C_NAME] \\
|
18 |
|
|
[inport=I_NAME] \\
|
19 |
|
|
[outmaskport=O_NAME] \\
|
20 |
|
|
[inmaskport=I_NAME] \\
|
21 |
|
|
[initmask=<value>]\n
|
22 |
|
|
Where:
|
23 |
|
|
insignal<N>={i_name|s__name}[,C_NAME]
|
24 |
|
|
specifies one of the eight possible single-bit signal that will trigger
|
25 |
|
|
the interrupt and optionally specify a constant defined by the interrupt
|
26 |
|
|
bit position
|
27 |
|
|
Note: Replace <N> with 0, 1, ..., 7
|
28 |
|
|
Note: If the signal name starts with "i_" it will be added as a single
|
29 |
|
|
bit wide input to the processor. If the signal name starts with
|
30 |
|
|
"s__" it must be a signal from another peripheral with the given
|
31 |
|
|
name.
|
32 |
|
|
Note: If the signal name is preceded by a an exclamation mark, i.e., a
|
33 |
|
|
"!", then the signal will be treated as falling-edge triggered
|
34 |
|
|
instead of rising edge triggered.
|
35 |
|
|
Note: External signals may need to be synchronized to the micro
|
36 |
|
|
controller clock.
|
37 |
|
|
inport=I_NAME
|
38 |
|
|
provide the input port name to read the interrupt signal
|
39 |
|
|
Note: This port is prohibited if there is only one interrupt signal and
|
40 |
|
|
it is required if there is more than one interrupt signal.
|
41 |
|
|
outmaskport=O_NAME
|
42 |
|
|
optionally specifies a port to provide an enable/disable mask to
|
43 |
|
|
the interrupt signals
|
44 |
|
|
inmaskport=I_NAME
|
45 |
|
|
optionally specifies a port to read the current enable/disable mask
|
46 |
|
|
Note: This cannot be used if outmaskport is not specified.
|
47 |
|
|
initmask=<value>
|
48 |
|
|
optionally specifies the initial value of the mask
|
49 |
|
|
Note: This cannot be used if outmaskport is not specified.
|
50 |
|
|
Note: The value is either a Verilog format value or a decimal value.
|
51 |
|
|
Note: If this is not specified then all interrupt bits will be enabled.\n
|
52 |
|
|
Example: Trigger an interrupt when a 1 clock wide strobe is received from an
|
53 |
|
|
external timer.\n
|
54 |
|
|
# In the architecture file
|
55 |
|
|
PERIPHERAL interrupt insignal0=i_timer_strobe\n
|
56 |
|
|
; In the assembly file
|
57 |
|
|
.interrupt
|
58 |
|
|
; Do the timer event.
|
59 |
|
|
...
|
60 |
|
|
; Return from the interrupt.
|
61 |
|
|
.returni\n
|
62 |
|
|
Example: Monitor an external timer strobe and a FIFO empty flag (which is
|
63 |
|
|
high when the FIFO is empty).\n
|
64 |
|
|
# In the architecture file
|
65 |
|
|
PERIPHERAL outFIFO_async data=o_fifo ... outempty=I_EMPTY
|
66 |
|
|
PERIPHERAL interrupt insignal0=!s__o_fifo__outempty_in,C_INTERRUPT_MASK_FIFO \\
|
67 |
|
|
insignal1=i_timer_strobe,C_INTERRUPT_MASK_TIMER \\
|
68 |
|
|
inport=I_INTERRUPT\n
|
69 |
|
|
; In the assembly file
|
70 |
|
|
.interrupt
|
71 |
|
|
; Read the interrupt condition.
|
72 |
|
|
.inport(I_INTERRUPT)
|
73 |
|
|
; If the interrupt was triggered by the timer, then handle the
|
74 |
|
|
; timer event (but don't throw away the rest of the interrupt
|
75 |
|
|
; condition).
|
76 |
|
|
dup C_INTERRUPT_MASK_TIMER & .callc(...)
|
77 |
|
|
; If the interrupt was triggered by the FIFO becoming empty, then
|
78 |
|
|
; do whatever's appropriate (and throw away the interrupt
|
79 |
|
|
; condition).
|
80 |
|
|
C_INTERRUPT_MASK_FIFO & .callc(...)
|
81 |
|
|
; Return from the interrupt.
|
82 |
|
|
.returni\n
|
83 |
|
|
WARNING: Setting the interrupt mask does not disable interrupts occuring
|
84 |
|
|
before a bit in the mask is cleared. I.e., if a particular
|
85 |
|
|
interrupt bit is disabled by a new interrupt mask, but an interrupt
|
86 |
|
|
for that bit occured before the mask bit was cleared, then that
|
87 |
|
|
interrupt bit will still have caused a pending interrupt. This is
|
88 |
|
|
particularly important when the processor is starting.\n
|
89 |
|
|
This can be resolved in part by using the following code at the top
|
90 |
|
|
of the interrupt handler:\n
|
91 |
|
|
.inport(I_INTERRUPT) .inport(I_INTERRUPT_MASK) &
|
92 |
|
|
where .inport(I_INTERRUPT) gets the interrupt event(s) and
|
93 |
|
|
.inport(I_INTERRUPT_MASK) gets the current interrupt mask.
|
94 |
|
|
"""
|
95 |
|
|
|
96 |
|
|
def __init__(self,peripheralFile,config,param_list,loc):
|
97 |
|
|
"""
|
98 |
|
|
Configure this peripheral as an interrupt peripheral.
|
99 |
|
|
"""
|
100 |
|
|
# Invoke the base class __init__ function before doing anything else.
|
101 |
|
|
SSBCCinterruptPeripheral.__init__(self,config,loc)
|
102 |
|
|
# Use the externally provided file name for the peripheral
|
103 |
|
|
self.peripheralFile = peripheralFile
|
104 |
|
|
# Get the parameters.
|
105 |
|
|
allowables = (
|
106 |
|
|
( 'initmask', r'\S+$', lambda v : self.IntValueMethod(v), ),
|
107 |
|
|
( 'inmaskport', r'I_\w+$', None, ),
|
108 |
|
|
( 'inport', r'I_\w+$', None, ),
|
109 |
|
|
( 'outmaskport', r'O_\w+$', None, ),
|
110 |
|
|
)
|
111 |
|
|
names = [a[0] for a in allowables]
|
112 |
|
|
self.insignal = [None for ix in range(config.Get('data_width'))]
|
113 |
|
|
self.invert = 0
|
114 |
|
|
for param_tuple in param_list:
|
115 |
|
|
param = param_tuple[0]
|
116 |
|
|
if re.match(r'insignal[0-7]$',param):
|
117 |
|
|
if param_tuple[1] == None:
|
118 |
|
|
raise SSBCCException('"%s" missing value at %s' % (param,loc,))
|
119 |
|
|
ix = int(param[-1])
|
120 |
|
|
if self.insignal[ix]:
|
121 |
|
|
raise SSBCCException('%s already specified at %s' % (param,loc,))
|
122 |
|
|
pars = re.findall(r'(!?)(i_\w+|s__\w+)(,C_\w+)?$',param_tuple[1])
|
123 |
|
|
if not pars or not pars[0][1]:
|
124 |
|
|
raise SSBCCException('I/O symbol at %s does not match required format "[!]{i_name|s__name}[,C_name]": "%s"' % (loc,param_tuple[1],))
|
125 |
|
|
pars = pars[0]
|
126 |
|
|
if pars[0]:
|
127 |
|
|
self.invert |= 2**ix
|
128 |
|
|
self.insignal[ix] = pars[1]
|
129 |
|
|
if pars[2]:
|
130 |
|
|
config.AddConstant(pars[2][1:],2**ix,loc)
|
131 |
|
|
elif param in names:
|
132 |
|
|
param_test = allowables[names.index(param)]
|
133 |
|
|
self.AddAttr(config,param,param_tuple[1],param_test[1],loc,param_test[2])
|
134 |
|
|
else:
|
135 |
|
|
raise SSBCCException('Unrecognized parameter "%s" at %s' % (param,loc,))
|
136 |
|
|
# Ensure the required parameters are set.
|
137 |
|
|
if not any(self.insignal):
|
138 |
|
|
raise SSBCCException('Required parameter insignal<N> missing at %s' % loc)
|
139 |
|
|
self.width = sum(1 for ix in range(len(self.insignal)) if self.insignal[ix])
|
140 |
|
|
ixMissing = [ix for ix in range(self.width) if not self.insignal[ix]]
|
141 |
|
|
if ixMissing:
|
142 |
|
|
raise SSBCCException('insignal%d missing at %s' % (ixMissing[0],loc,))
|
143 |
|
|
if self.width == 1:
|
144 |
|
|
if hasattr(self,'inport'):
|
145 |
|
|
raise SSBCCException('Parameter "inport" is prohibited when there is only one interrupt signal at %s' % loc)
|
146 |
|
|
else:
|
147 |
|
|
if not hasattr(self,'inport'):
|
148 |
|
|
raise SSBCCException('Required parameter "%s" is missing at %s' % (paramname,loc,))
|
149 |
|
|
# Ensure optional parameters are consistent.
|
150 |
|
|
for opt in ('inmaskport','initmask',):
|
151 |
|
|
if hasattr(self,opt) and not hasattr(self,'outmaskport'):
|
152 |
|
|
raise SSBCCException('Optional parameter "%s" requires "outmaskport" at %s' % (opt,loc,))
|
153 |
|
|
if hasattr(self,'initmask'):
|
154 |
|
|
if self.initmask >= 2**self.width:
|
155 |
|
|
raise SSBCCException('Value of "initmask" exceeds interrupt width at %s' % loc)
|
156 |
|
|
# Create the signal for the triggering interrupt source.
|
157 |
|
|
config.AddSignal('s_interrupt_trigger',self.width,loc)
|
158 |
|
|
# Add the I/O port, internal signals, and the INPORT and OUTPORT symbols for this peripheral.
|
159 |
|
|
for ix in [ix for ix in range(self.width) if re.match(r'i_',self.insignal[ix])]:
|
160 |
|
|
config.AddIO(self.insignal[ix],1,'input',loc)
|
161 |
|
|
if hasattr(self,'inport'):
|
162 |
|
|
self.ix_inport = config.NInports()
|
163 |
|
|
config.AddInport((self.inport,
|
164 |
|
|
('s_interrupt_trigger',self.width,'data',),
|
165 |
|
|
),
|
166 |
|
|
loc)
|
167 |
|
|
if not hasattr(self,'initmask'):
|
168 |
|
|
self.initmask= '%d\'h%X' % (self.width,2**self.width-1,)
|
169 |
|
|
if hasattr(self,'outmaskport'):
|
170 |
|
|
self.masksignal = 's_interrupt_mask'
|
171 |
|
|
config.AddSignalWithInit(self.masksignal,self.width,None,loc)
|
172 |
|
|
config.AddOutport((self.outmaskport,False,
|
173 |
|
|
(self.masksignal,self.width,'data',self.initmask,),
|
174 |
|
|
),
|
175 |
|
|
loc)
|
176 |
|
|
if hasattr(self,'inmaskport'):
|
177 |
|
|
config.AddInport((self.inmaskport,
|
178 |
|
|
(self.masksignal,self.width,'data',),
|
179 |
|
|
),
|
180 |
|
|
loc)
|
181 |
|
|
else:
|
182 |
|
|
self.masksignal = self.initmask
|
183 |
|
|
|
184 |
|
|
def GenVerilog(self,fp,config):
|
185 |
|
|
body = self.LoadCore(self.peripheralFile,'.v');
|
186 |
|
|
if self.width == 1:
|
187 |
|
|
body = re.sub(r'reg s_interrupt_trigger_any.*?\n','',body);
|
188 |
|
|
while re.search(r' {4,}s_interrupt_trigger_any',body):
|
189 |
|
|
body = re.sub(r' {4,}s_interrupt_trigger_any.*?\n','',body);
|
190 |
|
|
body = re.sub(r's_interrupt_trigger_any','s_interrupt_trigger',body);
|
191 |
|
|
if not hasattr(self,'inport'):
|
192 |
|
|
clear_trigger = 's_interrupt';
|
193 |
|
|
else:
|
194 |
|
|
clear_trigger = 's_inport && (s_T == %d)' % self.ix_inport;
|
195 |
|
|
for subpair in (
|
196 |
|
|
( r'@CLEAR_TRIGGER@', clear_trigger, ),
|
197 |
|
|
( r'@IX_OUTPORT_DIS@', '8\'h%02X' % self.ix_outport_interrupt_dis, ),
|
198 |
|
|
( r'@IX_OUTPORT_ENA@', '8\'h%02X' % self.ix_outport_interrupt_ena, ),
|
199 |
|
|
( r'@INSIGNAL@', '{ %s }' % ', '.join(self.insignal[ix] for ix in range(self.width-1,-1,-1)), ),
|
200 |
|
|
( r'@INVERT@', '%d\'h%X' % (self.width,self.invert,), ),
|
201 |
|
|
( r'@MASK@', self.masksignal, ),
|
202 |
|
|
( r'@WIDTH@ ', '' if self.width==1 else '[%d:0] ' % (self.width-1,), ),
|
203 |
|
|
( r'@ZERO@', '%d\'h0' % self.width, ),
|
204 |
|
|
):
|
205 |
|
|
body = re.sub(subpair[0],subpair[1],body);
|
206 |
|
|
body = self.GenVerilogFinal(config,body);
|
207 |
|
|
fp.write(body);
|