1 |
12 |
sinclairrf |
<!-- Copyright 2015, Sinclair R.F., Inc. -->
|
2 |
|
|
<html>
|
3 |
|
|
<title>
|
4 |
|
|
macros
|
5 |
|
|
</title>
|
6 |
|
|
<body>
|
7 |
|
|
<h1>Interrupt handlers for the 9x8 micro controller</h1><br/>
|
8 |
|
|
Copyright 2012, Sinclair R.F., Inc.<br/><br/>
|
9 |
|
|
This document describes how to implement interrupt handlers for the 9x8 micro
|
10 |
|
|
controller.<br/><br/>
|
11 |
|
|
There is a single interrupt in the controller, although this interrupt can be
|
12 |
|
|
triggered by more than one signal. Implementing interrupts consists of two
|
13 |
|
|
actions: adding a single interrupt peripheral to the processor architecture
|
14 |
|
|
and adding a ".interrupt" body to the assembly source.<br/><br/>
|
15 |
|
|
The interrupt test bench illustrates how to add an interrupt for a single
|
16 |
|
|
external event and the design in <tt>example/interrupt</tt> illustrates how to
|
17 |
|
|
add an interrupt for two events, one external to the processor and one
|
18 |
|
|
internal to the processor.<br/><br/>
|
19 |
|
|
<h2>Theory of Operation</h2>
|
20 |
|
|
The interrupt peripheral creates two signals, <tt>s_interrupt</tt> and
|
21 |
|
|
<tt>s_interrupted</tt>, for the interrupt event and to disable normal
|
22 |
|
|
processor operation until the interrupt handler is running.<br/><br/>
|
23 |
|
|
Specifically, <tt>s_interrupt</tt> is a non-registered signal that is high
|
24 |
|
|
when (1) interrupts are enabled, (2) an interrupt edge has occurred
|
25 |
|
|
(and not be precluded by the interrupt mask, if any), and (3) the
|
26 |
|
|
processor is not in the middle of executing a jump, call, or return.<br/><br/>
|
27 |
|
|
When <tt>s_interrupt</tt> goes high, the processor pushes the PC address for
|
28 |
|
|
the current instruction onto the return stack, sets the next PC address to be
|
29 |
|
|
the interrupt handler start address (so that the interrupt handler will start
|
30 |
|
|
executing in 2 instruction cycles), and otherwise performs a "nop."
|
31 |
|
|
Because of the piplined PC/opcode architecture, a delay register is required
|
32 |
|
|
for the current opcode PC address to be available.<br/><br/>
|
33 |
|
|
The instruction cycle after <tt>s_interrupt</tt> is high must perform a "nop."
|
34 |
|
|
This is done by using <tt>s_interrupted</tt> as a registered, delayed, version
|
35 |
|
|
of <tt>s_interrupt</tt>. When <tt>s_interrupted</tt> is high, the processor
|
36 |
|
|
core is coerced to perform a "nop" and the instruction pipeline architecture
|
37 |
|
|
starts fetching the second instruction in the interrupt handler.<br/><br/>
|
38 |
|
|
When the "return" opcode is performed by the interrupt handler, execution will
|
39 |
|
|
resume at the instruction that would have been performed when
|
40 |
|
|
<tt>s_interrupt</tt> was high. This instruction cannot be one immediately
|
41 |
|
|
after a <tt>jump</tt>, <tt>call</tt>, or <tt>return</tt> or one after a
|
42 |
|
|
<tt>jumpc</tt> or <tt>callc</tt> if the conditional was true, otherwise the
|
43 |
|
|
processor will not perform the desired jump, call, or return and will simply
|
44 |
|
|
start executing the code following the instruction after the jump, call, or
|
45 |
|
|
return.<br/><br/>
|
46 |
|
|
On return from the interrupt handler, the interrupts are enabled in a way
|
47 |
|
|
that precludes the interrupt handler from being interrupted again. This is
|
48 |
|
|
done with the three instruction sequence "<tt>O_INTERRUPT_ENA return
|
49 |
|
|
outport</tt>." The outport, as the instruction performed immediately after
|
50 |
|
|
the return, enables interrupts on the following instruction cycle, which will
|
51 |
|
|
be the first instruction cycle resuming the previous execution
|
52 |
|
|
sequence.<br/><br/>
|
53 |
|
|
The interrupt peripheral needs to generate the <tt>s_interrupt</tt> and
|
54 |
|
|
<tt>s_interrupted</tt> signals and the <tt>O_INTERRUPT_DIS</tt> and
|
55 |
|
|
<tt>O_INTERRUPT_ENA</tt> outport strobes; create signals for any interrupt
|
56 |
|
|
signals external to the processor; and instantiate the HDL for the
|
57 |
|
|
interrupt. Using the base class <tt>SSBCCinterruptPeripheral</tt> from
|
58 |
|
|
<tt>ssbccPeripheral</tt> ensures the <tt>s_interrupt</tt> and
|
59 |
|
|
<tt>s_interrupted</tt> signals are declared, although the code to generate
|
60 |
|
|
their values is not created, and it ensures the two outport strobes are
|
61 |
|
|
created.<br/><br/>
|
62 |
|
|
<h2>Example Implementation</h2>
|
63 |
|
|
The interrupt peripheral provided with the core provides an interface for one
|
64 |
|
|
to eight edge triggered interrupts. These interrupt sources can be external
|
65 |
|
|
to the processor or they can be signals from other peripherals. They are
|
66 |
|
|
normally rising edge triggered, but they can also be falling edge triggered.
|
67 |
|
|
The peripheral also provides an optional mask for the interrupt sources,
|
68 |
|
|
allowing it to be set, read, and initialized to a particular value.
|
69 |
|
|
Constants for bit maps for the interrupt signals can be defined as part of
|
70 |
|
|
selecting the signal for each of the one to eight interrupt signal
|
71 |
|
|
sources.<br/><br/>
|
72 |
|
|
The test bench for this interrupt peripheral illustrates a single, external,
|
73 |
|
|
rising-edge interrupt signal. The timing of the external interrupt was varied
|
74 |
|
|
to validate correct generation of the <tt>s_interrupt</tt> signal and return
|
75 |
|
|
from the interrupt handler (this was done by manually verifying the
|
76 |
|
|
displayed instruction sequences).<br/><br/>
|
77 |
|
|
An example interrupt controller for two interrupt signals, one external and
|
78 |
|
|
one internal, and one rising edge and one falled edge, along with a mask for
|
79 |
|
|
the interrupt signals, is also provided in
|
80 |
|
|
<tt>example/interrupt</tt>.<br/><br/>
|
81 |
|
|
<h2>Construction of Interrupt Peripherals</h2>
|
82 |
|
|
This discussion is based on the interrupt peripheral provided with the 9x8
|
83 |
|
|
processor core. It describes the HDL required to implement the interrupt
|
84 |
|
|
hardware.<br/><br/>
|
85 |
|
|
The processor core sets <tt>s_bus_pc</tt> to <tt>C_BUS_PC_JUMP</tt> when a
|
86 |
|
|
jump or call is performed and it sets it to <tt>C_BUS_PC_RETURN</tt> when a
|
87 |
|
|
return is being performed. When <tt>s_bus_pc</tt> is either one of these
|
88 |
|
|
values at the end of a processor clock cycle, then the instruction pipeline
|
89 |
|
|
will be in the middle of performing a jump, call, or return during the
|
90 |
|
|
following interval. During this subsequent interval, interrupts must be
|
91 |
|
|
disabled. This is done by capturing the status of <tt>s_bus_pc</tt> in the
|
92 |
|
|
register <tt>s_in_jump</tt> and prohibiting interrupts if <tt>s_in_jump</tt>
|
93 |
|
|
is high.<br/><br/>
|
94 |
|
|
The status of candidate interrupt signals is captured in
|
95 |
|
|
<tt>s_interrupt_raw</tt>. I.e., signal inversion is performed as required by
|
96 |
|
|
the peripheral architecture statement and masking is performed where the mask
|
97 |
|
|
is high if the signal is to be included as a candidate interrupt. The "raw"
|
98 |
|
|
interrupt triggers are then generated by looking for rising edges in this
|
99 |
|
|
signal as compared to the value(s) for the previous clock cycle.<br/><br/>
|
100 |
|
|
Two signals are then used to capture the trigger. The first,
|
101 |
|
|
<tt>s_interrupt_trigger</tt> records which enabled signals had a rising edge.
|
102 |
|
|
In order to reduced the depth of subsequent logic for the interrupt
|
103 |
|
|
signal itself, the single-bit signal <tt>s_interrupt_trigger_any</tt> records
|
104 |
|
|
whether or not any enabled signal had a rising edge. The history of both of
|
105 |
|
|
these signals is cleared if the processor reads the input port for
|
106 |
|
|
<tt>s_interrupt_trigger</tt>.<br/><br/>
|
107 |
|
|
The non-registered interrupt signal is then generated if (1) interrupts
|
108 |
|
|
are enabled, (2) a rising edge has occured, and (3) interrupts are
|
109 |
|
|
not disabled because the instruction pipeline is in the middle of a jump,
|
110 |
|
|
call, or return.<br/><br/>
|
111 |
|
|
A delayed version of <tt>s_interrupt</tt> is registed as
|
112 |
|
|
<tt>s_interrupted</tt> for generation of the interrupt-induced "nop"
|
113 |
|
|
instruction that must follow the interrupt.<br/><br/>
|
114 |
|
|
Finally, the interrupt enable signal is generated. Interrupts are initially
|
115 |
|
|
disabled (so that the processor can perform its initialization without
|
116 |
|
|
spurious stated induced by premature interrupts). Interrupts are then
|
117 |
|
|
disabled when an interrupt occurs or when the <tt>O_INTERRUPT_DIS</tt> strobe
|
118 |
|
|
is received. Interrupts are only enabled when the <tt>O_INTERRUPT_ENA</tt>
|
119 |
|
|
strobe is received.<br/><br/>
|
120 |
|
|
<h2>Construction of Interrupt Handlers</h2>
|
121 |
|
|
If there is only one signal that can produce an interrupt (as set in the
|
122 |
|
|
peripheral architecture statement), then the interrupt handler simply
|
123 |
|
|
processed the interrupt and exits using the <tt>.returni</tt> macro. For
|
124 |
|
|
example, the following code simply counts the number of interrupts received
|
125 |
|
|
(provided that they don't occur so fast that the interrupt handler isn't
|
126 |
|
|
called as fast as the interrupts occur):<br/><br/>
|
127 |
|
|
<tt> .interrupt<br/></tt>
|
128 |
|
|
<tt> .fetchvalue(interruptCount) 1+ .storevalue(interruptCount)<br/></tt>
|
129 |
|
|
<tt> .returni<br/></tt><br/>
|
130 |
|
|
If there is more than one signal that can produce an interrupt, then the
|
131 |
|
|
construction of the interrupt handler is slightly more complicated. Suppose
|
132 |
|
|
the interrupt peripheral architecture statement is:<br/><br/>
|
133 |
|
|
<tt> PERIPHERAL interrupt insignal0=i_int0,C_INT0 \<br/></tt>
|
134 |
|
|
<tt> insignal1=i_int1,C_INT1 \<br/></tt>
|
135 |
|
|
<tt> inport=I_INTERRUPT \<br/></tt>
|
136 |
|
|
<tt> ...<br/></tt><br/>
|
137 |
|
|
The interrupt handler then reads the interrupt trigger, conditionally calls
|
138 |
|
|
subroutines for the appropriate interrupt, clears the trigger from the data
|
139 |
|
|
stack, and returns as follows:<br/><br/>
|
140 |
|
|
<tt> .interrupt<br/></tt>
|
141 |
|
|
<tt> .inport(I_INTERRUPT)<br/></tt>
|
142 |
|
|
<tt> dup C_INT0 & .callc(int0)<br/></tt>
|
143 |
|
|
<tt> dup C_INT1 & .callc(int1)<br/></tt>
|
144 |
|
|
<tt> drop<br/></tt>
|
145 |
|
|
<tt> .returni<br/></tt><br/>
|
146 |
|
|
</body>
|
147 |
|
|
</html>
|