-
Notifications
You must be signed in to change notification settings - Fork 0
/
interrupt_template.c
350 lines (246 loc) · 11.8 KB
/
interrupt_template.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/*===================================================================
Template single/combined C file for ELEC 371 laboratory activity
October 2021 Dr. N. Manjikian
C programs for Nios II systems can be prepared with multiple
separate files in project defined with the Monitor Program.
A single C file, however, is necessary for the Web-based simulator
because the current version of the Web-based tool handles a single
file in its editor window.
This single file merges together several .h header files and
the exception_handler.c file with the assembly-language code
for saving/restoring registers and handling the ea adjustment.
The .h header file contents are positioned at the beginning of
this template file, and the exception_handler.c file contents
are positioned at the end of this template file. There are
comments with "----" dividers and original-file identification
to aid in understanding the different portions.
In the middle of this template file is the portion that can be
customized for a particular application. For interrupt support,
there is an empty interrupt_handler() function. There is an
empty Init() function and a simple main() function. Other
functions can be added as necessary in front of main().
==================================================================*/
/*-----------------------------------------------------------------*/
#ifndef _NIOS2_CONTROL_H_
#define _NIOS2_CONTROL_H_
/*
This file defines macros that utilize the built-in functions
in Altera's adaption of the GCC compiler to generate the
control-register-access instructions for the Nios II processor.
For ELEC 371 laboratory work, it has been modified by N. Manjikian.
It should be noted that the manner in which the built-in functions
are used below _differs_ from the manner in which they are shown in
the DE0 Basic Computer reference document from Altera. The key
difference is for _read_ operations. The conventional method of
defining macros that read a value is to have the value returned,
like a function. The method used in the original Altera code is
not conventional. The second main difference is a simplification
that avoids the use of a do..while loop found in the Altera version.
The do..while loops may have been introduced for a specific reason (e.g.,
to perhaps prevent the compiler from optimizing away the calls to
the built-in functions, or to allow the assignment of the return
value to a named variable). For the purposes of laboratory work,
the revision of the macros by the instructor is appropriate, and
the compiler-generated code is correct.
The official names for the processor control register are ctl0, ctl1, ...
These registers are more conveniently identified by their aliases,
such as status, estatus, ... Documentation on the Nios II processor
provides the association between the official names and the aliases.
Not all of the possible control-register accesses are covered by
the macros below. The ones most relevant for ELEC 371 laboratory work
are included. If access to the other control registers is required,
additional macros could easily be introduced, based on the existing ones.
The number on the right side of each macro definition refers to the
corresponding 'ctl_' processor control register. The __builtin_wrctl()
function passes the ctl register number and a value to the compiler for
use in generating the appropriate assembly-language instruction.
The __builtin_rdctl() function passes only the register number, and
it _returns_ a value. The instructor-revised read macros then return
this result as the return value of the macro for use by the C program.
*/
#define NIOS2_WRITE_STATUS(value) (__builtin_wrctl (0, value))
#define NIOS2_READ_IENABLE() (__builtin_rdctl (3))
#define NIOS2_WRITE_IENABLE(value) (__builtin_wrctl (3, value))
#define NIOS2_READ_IPENDING() (__builtin_rdctl (4))
#endif /* _NIOS2_CONTROL_H_ */
/*-----------------------------------------------------------------*/
#ifndef _TIMER_H_
#define _TIMER_H_
/* define pointer macros for accessing the timer interface registers */
#define TIMER_STATUS ((volatile unsigned int *) 0x10002000)
#define TIMER_CONTROL ((volatile unsigned int *) 0x10002004)
#define TIMER_START_LO ((volatile unsigned int *) 0x10002008)
#define TIMER_START_HI ((volatile unsigned int *) 0x1000200C)
#define TIMER_SNAP_LO ((volatile unsigned int *) 0x10002010)
#define TIMER_SNAP_HI ((volatile unsigned int *) 0x10002014)
/* define a bit pattern reflecting the position of the timeout (TO) bit
in the timer status register */
#define TIMER_TO_BIT 0x1
#endif /* _TIMER_H_ */
/*-----------------------------------------------------------------*/
#ifndef _LEDS_H_
#define _LEDS_H_
/* define pointer macro for accessing the LED interface data register */
#define LEDS ((volatile unsigned int *) 0x10000010)
#endif /* _LEDS_H_ */
/*-----------------------------------------------------------------*/
/* start of application-specific code */
/*-----------------------------------------------------------------*/
/* place additional #define macros here */
/* define global program variables here */
void interrupt_handler(void)
{
unsigned int ipending;
/* read current value in ipending register */
/* do one or more checks for different sources using ipending value */
/* remember to clear interrupt sources */
}
void Init (void)
{
/* initialize software variables */
/* set up each hardware interface */
/* set up ienable */
/* enable global recognition of interrupts in procr. status reg. */
}
/* place additional functions here */
int main (void)
{
Init (); /* perform software/hardware initialization */
while (1)
{
/* fill in body of infinite loop */
}
return 0; /* never reached, but main() must return a value */
}
/*-----------------------------------------------------------------*/
/* end of application-specific code */
/*-----------------------------------------------------------------*/
/*-----------------------------------------------------------------*/
/*
exception_handler.c
This file is a portion of the original code supplied by Altera.
It has been adapted by N. Manjikian for use in ELEC 371 laboratory work.
Various unnecessary or extraneous elements have been excluded. For
example, declarations in C for external functions called from asm()
instructions are not required because any reference to external names
in asm() instructions is embedded directly in the output written to
the assembly-language .s file without any other checks by the C compiler.
There is one particularly important change: on _reset_, the jump must be
to the >> _start << location in order to properly initialize the stack
pointer and to perform other crucial initialization tasks that ensure
proper C semantics for variable initialization are enforced. The Altera
version of the code jumped to main(), which will _not_ perform these
crucial initialization tasks correctly.
Finally, a reference to control register 'ctl4' in the asm() sequence
has been replaced with the more meaningful alias 'ipending' for clarity.
Other than the changes described above, the file contents have also been
reformatted to fit in 80 columns of text, and comments have been edited.
*/
/* The assembly language code below handles processor reset */
void the_reset (void) __attribute__ ((section (".reset")));
/*****************************************************************************
* Reset code. By giving the code a section attribute with the name ".reset" *
* we allow the linker program to locate this code at the proper reset vector*
* address. This code jumps to _startup_ code for C program, _not_ main(). *
*****************************************************************************/
void the_reset (void)
{
asm (".set noat"); /* the .set commands are included to prevent */
asm (".set nobreak"); /* warning messages from the assembler */
asm ("movia r2, _start"); /* jump to the C language _startup_ code */
asm ("jmp r2"); /* (_not_ main, as in the original Altera file) */
}
/* The assembly language code below handles exception processing. This
* code should not be modified; instead, the C language code in the normal
* function interrupt_handler() [which is called from the code below]
* can be modified as needed for a given application.
*/
void the_exception (void) __attribute__ ((section (".exceptions")));
/*****************************************************************************
* Exceptions code. By giving the code a section attribute with the name *
* ".exceptions" we allow the linker program to locate this code at the *
* proper exceptions vector address. This code calls the interrupt handler *
* and later returns from the exception to the main program. *
*****************************************************************************/
void the_exception (void)
{
asm (".set noat"); /* the .set commands are included to prevent */
asm (".set nobreak"); /* warning messages from the assembler */
asm ("subi sp, sp, 128");
asm ("stw et, 96(sp)");
asm ("rdctl et, ipending"); /* changed 'ctl4' to 'ipending' for clarity */
asm ("beq et, r0, SKIP_EA_DEC"); /* Not a hardware interrupt, */
asm ("subi ea, ea, 4"); /* so decrement ea by one instruction */
asm ("SKIP_EA_DEC:");
asm ("stw r1, 4(sp)"); /* Save all registers */
asm ("stw r2, 8(sp)");
asm ("stw r3, 12(sp)");
asm ("stw r4, 16(sp)");
asm ("stw r5, 20(sp)");
asm ("stw r6, 24(sp)");
asm ("stw r7, 28(sp)");
asm ("stw r8, 32(sp)");
asm ("stw r9, 36(sp)");
asm ("stw r10, 40(sp)");
asm ("stw r11, 44(sp)");
asm ("stw r12, 48(sp)");
asm ("stw r13, 52(sp)");
asm ("stw r14, 56(sp)");
asm ("stw r15, 60(sp)");
asm ("stw r16, 64(sp)");
asm ("stw r17, 68(sp)");
asm ("stw r18, 72(sp)");
asm ("stw r19, 76(sp)");
asm ("stw r20, 80(sp)");
asm ("stw r21, 84(sp)");
asm ("stw r22, 88(sp)");
asm ("stw r23, 92(sp)");
asm ("stw r25, 100(sp)"); /* r25 = bt (r24 = et, saved above) */
asm ("stw r26, 104(sp)"); /* r26 = gp */
/* skip saving r27 because it is sp, and there is no point in saving sp */
asm ("stw r28, 112(sp)"); /* r28 = fp */
asm ("stw r29, 116(sp)"); /* r29 = ea */
asm ("stw r30, 120(sp)"); /* r30 = ba */
asm ("stw r31, 124(sp)"); /* r31 = ra */
asm ("addi fp, sp, 128"); /* frame pointer adjustment */
asm ("call interrupt_handler"); /* call normal function */
asm ("ldw r1, 4(sp)"); /* Restore all registers */
asm ("ldw r2, 8(sp)");
asm ("ldw r3, 12(sp)");
asm ("ldw r4, 16(sp)");
asm ("ldw r5, 20(sp)");
asm ("ldw r6, 24(sp)");
asm ("ldw r7, 28(sp)");
asm ("ldw r8, 32(sp)");
asm ("ldw r9, 36(sp)");
asm ("ldw r10, 40(sp)");
asm ("ldw r11, 44(sp)");
asm ("ldw r12, 48(sp)");
asm ("ldw r13, 52(sp)");
asm ("ldw r14, 56(sp)");
asm ("ldw r15, 60(sp)");
asm ("ldw r16, 64(sp)");
asm ("ldw r17, 68(sp)");
asm ("ldw r18, 72(sp)");
asm ("ldw r19, 76(sp)");
asm ("ldw r20, 80(sp)");
asm ("ldw r21, 84(sp)");
asm ("ldw r22, 88(sp)");
asm ("ldw r23, 92(sp)");
asm ("ldw r24, 96(sp)");
asm ("ldw r25, 100(sp)");
asm ("ldw r26, 104(sp)");
/* skip r27 because it is sp, and we did not save this on the stack */
asm ("ldw r28, 112(sp)");
asm ("ldw r29, 116(sp)");
asm ("ldw r30, 120(sp)");
asm ("ldw r31, 124(sp)");
asm ("addi sp, sp, 128");
asm ("eret"); /* return from exception */
/* Note that the C compiler will still generate the 'standard'
end-of-normal-function code with a normal return-from-subroutine
instruction. But with the above eret instruction embedded
in the final output from the compiler, that end-of-function code
will never be executed.
*/
}