1 |
54 |
dgisselq |
////////////////////////////////////////////////////////////////////////////////
|
2 |
|
|
//
|
3 |
|
|
// Filename: crt0.c
|
4 |
|
|
//
|
5 |
|
|
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core
|
6 |
|
|
//
|
7 |
|
|
// Purpose: To start a program from flash, loading its various components
|
8 |
|
|
// into on-chip block RAM, or off-chip DDR3 SDRAM, as indicated
|
9 |
|
|
// by the symbols/pointers within the program itself. As you will notice
|
10 |
|
|
// by the names of the symbols, it is assumed that a kernel will be placed
|
11 |
|
|
// into block RAM.
|
12 |
|
|
//
|
13 |
|
|
// This particular implementation depends upon the following symbols
|
14 |
|
|
// being defined:
|
15 |
|
|
//
|
16 |
|
|
// int main(int argc, char **argv)
|
17 |
|
|
// The location where your program will start from, once fully
|
18 |
|
|
// loaded. argc will always be set to zero, and ARGV to a pointer
|
19 |
|
|
// to zero.
|
20 |
|
|
//
|
21 |
|
|
// _top_of_stack:
|
22 |
|
|
// A pointer to a location in memory which we can use for a stack.
|
23 |
|
|
// The bootloader doesn't use much of this memory, although it does
|
24 |
|
|
// use it. It then resets the stack to this location and calls
|
25 |
|
|
// your program.
|
26 |
|
|
//
|
27 |
|
|
// _top_of_heap:
|
28 |
|
|
// While not used by this program, this is assumed to be defined
|
29 |
|
|
// by the linker as the lowest memory address in a space that can
|
30 |
|
|
// be used by a malloc/free restore capability.
|
31 |
|
|
//
|
32 |
|
|
// _flash:
|
33 |
|
|
// The address of the beginning of physical FLASH device. This is
|
34 |
|
|
// not the first usable address on that device, as that is often
|
35 |
|
|
// reserved for the first two FPGA configurations.
|
36 |
|
|
//
|
37 |
|
|
// _blkram:
|
38 |
|
|
// The first address of the block RAM memory within the FPGA.
|
39 |
|
|
//
|
40 |
|
|
// _sdram:
|
41 |
|
|
// The address of the beginning of physical SDRAM.
|
42 |
|
|
//
|
43 |
|
|
// _kernel_image_start:
|
44 |
|
|
// The address of that location within FLASH where the sections
|
45 |
|
|
// needing to be moved begin at.
|
46 |
|
|
//
|
47 |
|
|
// _kernel_image_end:
|
48 |
|
|
// The last address within block RAM that needs to be filled in.
|
49 |
|
|
//
|
50 |
|
|
// _sdram_image_start:
|
51 |
|
|
// This address is more confusing. This is equal to one past the
|
52 |
|
|
// last used block RAM address, or the last used flash address if
|
53 |
|
|
// no block RAM is used. It is used for determining whether or not
|
54 |
|
|
// block RAM was used at all.
|
55 |
|
|
//
|
56 |
|
|
// _sdram_image_end:
|
57 |
|
|
// This is one past the last address in SDRAM that needs to be
|
58 |
|
|
// set with valid data.
|
59 |
|
|
//
|
60 |
|
|
// This pointer is made even more confusing by the fact that,
|
61 |
|
|
// if there is nothing allocated in SDRAM, this pointer will
|
62 |
|
|
// still point to block RAM. To make matters worse, the MAP
|
63 |
|
|
// file won't match the pointer in memory. (I spent three days
|
64 |
|
|
// trying to chase this down, and came up empty. Basically,
|
65 |
|
|
// the BFD structures may set this to point to block RAM, whereas
|
66 |
|
|
// the MAP--which uses different data and different methods of
|
67 |
|
|
// computation--may leave this pointing to SDRAM. Go figure.)
|
68 |
|
|
//
|
69 |
|
|
// _bss_image_end:
|
70 |
|
|
// This is the last address of memory that must be cleared upon
|
71 |
|
|
// startup, for which the program is assuming that it is zero.
|
72 |
|
|
// While it may not be necessary to clear the BSS memory, since
|
73 |
|
|
// BSS memory is always zero on power up, this bootloader does so
|
74 |
|
|
// anyway--since we might be starting from a reset instead of power
|
75 |
|
|
// up.
|
76 |
|
|
//
|
77 |
|
|
//
|
78 |
|
|
//
|
79 |
|
|
// Creator: Dan Gisselquist, Ph.D.
|
80 |
|
|
// Gisselquist Technology, LLC
|
81 |
|
|
//
|
82 |
|
|
////////////////////////////////////////////////////////////////////////////////
|
83 |
|
|
//
|
84 |
|
|
// Copyright (C) 2017, Gisselquist Technology, LLC
|
85 |
|
|
//
|
86 |
|
|
// This program is free software (firmware): you can redistribute it and/or
|
87 |
|
|
// modify it under the terms of the GNU General Public License as published
|
88 |
|
|
// by the Free Software Foundation, either version 3 of the License, or (at
|
89 |
|
|
// your option) any later version.
|
90 |
|
|
//
|
91 |
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
92 |
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
|
93 |
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
94 |
|
|
// for more details.
|
95 |
|
|
//
|
96 |
|
|
// License: GPL, v3, as defined and found on www.gnu.org,
|
97 |
|
|
// http://www.gnu.org/licenses/gpl.html
|
98 |
|
|
//
|
99 |
|
|
//
|
100 |
|
|
////////////////////////////////////////////////////////////////////////////////
|
101 |
|
|
//
|
102 |
|
|
//
|
103 |
|
|
#include "zipcpu.h"
|
104 |
|
|
#include "artyboard.h" // Our current board support file
|
105 |
|
|
#include "bootloader.h"
|
106 |
|
|
|
107 |
|
|
// A bootloader is about nothing more than copying memory from a couple
|
108 |
|
|
// particular locations (Flash/ROM) to other locations in memory (BLKRAM
|
109 |
|
|
// and SDRAM). Our DMA is a hardware accelerator that does nothing but
|
110 |
|
|
// copy memory from one location to another. Why not use the DMA for this
|
111 |
|
|
// purpose then?
|
112 |
|
|
//
|
113 |
|
|
// Here, we have a USE_DMA #define. When this is defined, the memory copy
|
114 |
|
|
// will be done using the DMA hardware accelerator. This is a good thing,
|
115 |
|
|
// and this should be defined. There are two problems with defining this
|
116 |
|
|
// however: 1) It obscures for any readers of this code what is actually
|
117 |
|
|
// happening, and 2) it makes the code dependent upon yet another piece of the
|
118 |
|
|
// hardware design working. For these reasons, we allow you to turn it off.
|
119 |
|
|
#ifdef _HAS_ZIPSYS_DMA
|
120 |
|
|
#define USE_DMA
|
121 |
|
|
#endif
|
122 |
|
|
|
123 |
|
|
//
|
124 |
|
|
// _start:
|
125 |
|
|
//
|
126 |
|
|
// Every computer needs to start processing from somewhere on reboot, and every
|
127 |
|
|
// program needs some entry point. For the ZipCPU, that starting place is the
|
128 |
|
|
// routine with the _start symbol. It is important that this start symbol be
|
129 |
|
|
// placed at the boot address for the CPU. This is the very first address of
|
130 |
|
|
// program memory, and (currently) on the Arty board it is placed in Flash at
|
131 |
|
|
// _start = 0x4e0000. To make certain this routine goes into the very first
|
132 |
|
|
// address in flash, we place it into it's own special section, the .start
|
133 |
|
|
// section, and then tell the linker that the .start section is the first
|
134 |
|
|
// section where it needs to start placing code.
|
135 |
|
|
//
|
136 |
|
|
// If you read through this short assembly routine below, you'll find that it
|
137 |
|
|
// does only a small number of tasks. It sets the stack pointer to point to
|
138 |
|
|
// the top of the stack (a symbol defined in the linker file), calls the
|
139 |
|
|
// bootloader, resets the stack pointer, clears any data cache, and then calls
|
140 |
|
|
// the kernel entry function. It also sets up a return address for the kernel
|
141 |
|
|
// entry function so that, should the kernel ever exit, it wouldn't exit on
|
142 |
|
|
// any error but rather it would exit by halting the CPU.
|
143 |
|
|
//
|
144 |
|
|
asm("\t.section\t.start,\"ax\",@progbits\n"
|
145 |
|
|
"\t.global\t_start\n"
|
146 |
|
|
"_start:" "\t; Here's the global ZipCPU entry point upon reset/reboot\n"
|
147 |
|
|
"\tLDI\t_top_of_stack,SP" "\t; Set up our supervisor stack ptr\n"
|
148 |
|
|
"\tMOV\t_kernel_is_dead(PC),uPC" "\t; Set user PC pointer to somewhere valid\n"
|
149 |
|
|
#ifndef SKIP_BOOTLOADER
|
150 |
|
|
"\tMOV\t_after_bootloader(PC),R0" " ; JSR to the bootloader routine\n"
|
151 |
|
|
"\tBRA\t_bootloader\n"
|
152 |
|
|
"_after_bootloader:\n"
|
153 |
|
|
"\tLDI\t_top_of_stack,SP" "\t; Set up our supervisor stack ptr\n"
|
154 |
|
|
"\tOR\t0x4000,CC" "\t; Clear the data cache\n"
|
155 |
|
|
#endif
|
156 |
|
|
#ifdef __USE_INIT_FINIT
|
157 |
|
|
"\tJSR\tinit" "\t; Initialize any constructor code\n"
|
158 |
|
|
//
|
159 |
|
|
"\tLDI\tfini,R1" "\t; \n"
|
160 |
|
|
"\tJSR\t_atexit" "\t; Initialize any constructor code\n"
|
161 |
|
|
#endif
|
162 |
|
|
//
|
163 |
|
|
"\tCLR\tR1" "\t; argc = 0\n"
|
164 |
|
|
"\tMOV\t_argv(PC),R2" "\t; argv = &0\n"
|
165 |
|
|
"\tLDI\t__env,R3" "\t; env = NULL\n"
|
166 |
|
|
"\tJSR\tmain" "\t; Call the user main() function\n"
|
167 |
|
|
//
|
168 |
|
|
"_graceful_kernel_exit:" "\t; Halt on any return from main--gracefully\n"
|
169 |
|
|
"\tJSR\texit\n" "\t; Call the _exit as part of exiting\n"
|
170 |
|
|
"\t.global\t_exit\n"
|
171 |
|
|
"_exit:\n"
|
172 |
|
|
#ifdef _HAS_WBUART
|
173 |
|
|
"\tLW 0x45c,R2\n"
|
174 |
|
|
"\tTEST 0xffc,R2\n"
|
175 |
|
|
"\tBNZ _exit\n"
|
176 |
|
|
"\tLSR 16,R2\n"
|
177 |
|
|
"\tTEST 0xffc,R2\n"
|
178 |
|
|
"\tBNZ _exit\n"
|
179 |
|
|
#endif
|
180 |
|
|
"\tNEXIT\tR1\n" "\t; If in simulation, call an exit function\n"
|
181 |
|
|
"_kernel_is_dead:" "\t; Halt the CPU\n"
|
182 |
|
|
"\tHALT\n" "\t; We should *never* continue following a\n"
|
183 |
|
|
"\tBRA\t_kernel_is_dead" "\t; halt, do something useful if so ??\n"
|
184 |
|
|
"_argv:\n"
|
185 |
|
|
"\t.WORD\t0,0\n"
|
186 |
|
|
"\t.section\t.text");
|
187 |
|
|
|
188 |
|
|
//
|
189 |
|
|
// We need to insist that the bootloader be kept in Flash, else it would depend
|
190 |
|
|
// upon running a routine from memory that ... wasn't in memory yet. For this
|
191 |
|
|
// purpose, we place the bootloader in a special .boot section. We'll also tell
|
192 |
|
|
// the linker, via the linker script, that this .boot section needs to be placed
|
193 |
|
|
// into flash.
|
194 |
|
|
//
|
195 |
|
|
extern void _bootloader(void) __attribute__ ((section (".boot")));
|
196 |
|
|
|
197 |
|
|
//
|
198 |
|
|
// bootloader()
|
199 |
|
|
//
|
200 |
|
|
// Here's the actual boot loader itself. It copies three areas from flash:
|
201 |
|
|
// 1. An area from flash to block RAM
|
202 |
|
|
// 2. A second area from flash to SDRAM
|
203 |
|
|
// 3. The third area isn't copied from flash, but rather it is just set to
|
204 |
|
|
// zero. This is sometimes called the BSS segment.
|
205 |
|
|
//
|
206 |
|
|
#ifndef SKIP_BOOTLOADER
|
207 |
|
|
void _bootloader(void) {
|
208 |
|
|
int *sdend = _sdram_image_end, *bsend = _bss_image_end;
|
209 |
|
|
if (sdend < _sdram)
|
210 |
|
|
sdend = _sdram; // R7
|
211 |
|
|
if (bsend < sdend)
|
212 |
|
|
bsend = sdend; // R6
|
213 |
|
|
|
214 |
|
|
#ifdef USE_DMA
|
215 |
|
|
zip->z_dma.d_ctrl= DMACLEAR;
|
216 |
|
|
zip->z_dma.d_rd = _kernel_image_start;
|
217 |
|
|
if (_kernel_image_end != _blkram) {
|
218 |
|
|
zip->z_dma.d_len = _kernel_image_end - _blkram;
|
219 |
|
|
zip->z_dma.d_wr = _blkram;
|
220 |
|
|
zip->z_dma.d_ctrl= DMACCOPY;
|
221 |
|
|
|
222 |
|
|
zip->z_pic = SYSINT_DMAC;
|
223 |
|
|
while((zip->z_pic & SYSINT_DMAC)==0)
|
224 |
|
|
;
|
225 |
|
|
}
|
226 |
|
|
|
227 |
|
|
// zip->z_dma.d_rd // Keeps the same value
|
228 |
|
|
zip->z_dma.d_wr = _sdram;
|
229 |
|
|
if (sdend != _sdram) {
|
230 |
|
|
zip->z_dma.d_len = sdend - _sdram;
|
231 |
|
|
zip->z_dma.d_ctrl= DMACCOPY;
|
232 |
|
|
|
233 |
|
|
zip->z_pic = SYSINT_DMAC;
|
234 |
|
|
while((zip->z_pic & SYSINT_DMAC)==0)
|
235 |
|
|
;
|
236 |
|
|
}
|
237 |
|
|
|
238 |
|
|
if (bsend != sdend) {
|
239 |
|
|
volatile int zero = 0;
|
240 |
|
|
|
241 |
|
|
zip->z_dma.d_len = bsend - sdend;
|
242 |
|
|
zip->z_dma.d_rd = (unsigned *)&zero;
|
243 |
|
|
// zip->z_dma.wr // Keeps the same value
|
244 |
|
|
zip->z_dma.d_ctrl = DMACCOPY|DMA_CONSTSRC;
|
245 |
|
|
|
246 |
|
|
zip->z_pic = SYSINT_DMAC;
|
247 |
|
|
while((zip->z_pic & SYSINT_DMAC)==0)
|
248 |
|
|
;
|
249 |
|
|
}
|
250 |
|
|
#else
|
251 |
|
|
int *rdp = _kernel_image_start, *wrp = _blkram;
|
252 |
|
|
|
253 |
|
|
if ((((int)wrp) & -4)==0)
|
254 |
|
|
wrp = _sdram;
|
255 |
|
|
|
256 |
|
|
//
|
257 |
|
|
// Load any part of the image into block RAM, but *only* if there's a
|
258 |
|
|
// block RAM section in the image. Based upon our LD script, the
|
259 |
|
|
// block RAM should be filled from _blkram to _kernel_image_end.
|
260 |
|
|
// It starts at _kernel_image_start --- our last valid address within
|
261 |
|
|
// the flash address region.
|
262 |
|
|
//
|
263 |
|
|
if (_kernel_image_end != _kernel_image_start) {
|
264 |
|
|
while(wrp < _kernel_image_end)
|
265 |
|
|
*wrp++ = *rdp++;
|
266 |
|
|
if (_kernel_image_end < _sdram)
|
267 |
|
|
wrp = _sdram;
|
268 |
|
|
}
|
269 |
|
|
|
270 |
|
|
//
|
271 |
|
|
// Now, we move on to the SDRAM image. We'll here load into SDRAM
|
272 |
|
|
// memory up to the end of the SDRAM image, _sdram_image_end.
|
273 |
|
|
// As with the last pointer, this one is also created for us by the
|
274 |
|
|
// linker.
|
275 |
|
|
//
|
276 |
|
|
// while(wrp < sdend) // Could also be done this way ...
|
277 |
|
|
for(int i=0; i< sdend - _sdram; i++)
|
278 |
|
|
*wrp++ = *rdp++;
|
279 |
|
|
|
280 |
|
|
//
|
281 |
|
|
// Finally, we load BSS. This is the segment that only needs to be
|
282 |
|
|
// cleared to zero. It is available for global variables, but some
|
283 |
|
|
// initialization is expected within it. We start writing where
|
284 |
|
|
// the valid SDRAM context, i.e. the non-zero contents, end.
|
285 |
|
|
//
|
286 |
|
|
for(int i=0; i<bsend - sdend; i++)
|
287 |
|
|
*wrp++ = 0;
|
288 |
|
|
|
289 |
|
|
#endif
|
290 |
|
|
}
|
291 |
|
|
#endif
|
292 |
|
|
|