]>
Commit | Line | Data |
---|---|---|
9ee6e8bb PB |
1 | /* |
2 | * ARMV7M System emulation. | |
3 | * | |
4 | * Copyright (c) 2006-2007 CodeSourcery. | |
5 | * Written by Paul Brook | |
6 | * | |
7 | * This code is licenced under the GPL. | |
8 | */ | |
9 | ||
10 | #include "vl.h" | |
11 | ||
12 | /* Bitbanded IO. Each word corresponds to a single bit. */ | |
13 | ||
14 | /* Get the byte address of the real memory for a bitband acess. */ | |
15 | static inline uint32_t bitband_addr(uint32_t addr) | |
16 | { | |
17 | uint32_t res; | |
18 | ||
19 | res = addr & 0xe0000000; | |
20 | res |= (addr & 0x1ffffff) >> 5; | |
21 | return res; | |
22 | ||
23 | } | |
24 | ||
25 | static uint32_t bitband_readb(void *opaque, target_phys_addr_t offset) | |
26 | { | |
27 | uint8_t v; | |
28 | cpu_physical_memory_read(bitband_addr(offset), &v, 1); | |
29 | return (v & (1 << ((offset >> 2) & 7))) != 0; | |
30 | } | |
31 | ||
32 | static void bitband_writeb(void *opaque, target_phys_addr_t offset, | |
33 | uint32_t value) | |
34 | { | |
35 | uint32_t addr; | |
36 | uint8_t mask; | |
37 | uint8_t v; | |
38 | addr = bitband_addr(offset); | |
39 | mask = (1 << ((offset >> 2) & 7)); | |
40 | cpu_physical_memory_read(addr, &v, 1); | |
41 | if (value & 1) | |
42 | v |= mask; | |
43 | else | |
44 | v &= ~mask; | |
45 | cpu_physical_memory_write(addr, &v, 1); | |
46 | } | |
47 | ||
48 | static uint32_t bitband_readw(void *opaque, target_phys_addr_t offset) | |
49 | { | |
50 | uint32_t addr; | |
51 | uint16_t mask; | |
52 | uint16_t v; | |
53 | addr = bitband_addr(offset) & ~1; | |
54 | mask = (1 << ((offset >> 2) & 15)); | |
55 | mask = tswap16(mask); | |
56 | cpu_physical_memory_read(addr, (uint8_t *)&v, 2); | |
57 | return (v & mask) != 0; | |
58 | } | |
59 | ||
60 | static void bitband_writew(void *opaque, target_phys_addr_t offset, | |
61 | uint32_t value) | |
62 | { | |
63 | uint32_t addr; | |
64 | uint16_t mask; | |
65 | uint16_t v; | |
66 | addr = bitband_addr(offset) & ~1; | |
67 | mask = (1 << ((offset >> 2) & 15)); | |
68 | mask = tswap16(mask); | |
69 | cpu_physical_memory_read(addr, (uint8_t *)&v, 2); | |
70 | if (value & 1) | |
71 | v |= mask; | |
72 | else | |
73 | v &= ~mask; | |
74 | cpu_physical_memory_write(addr, (uint8_t *)&v, 2); | |
75 | } | |
76 | ||
77 | static uint32_t bitband_readl(void *opaque, target_phys_addr_t offset) | |
78 | { | |
79 | uint32_t addr; | |
80 | uint32_t mask; | |
81 | uint32_t v; | |
82 | addr = bitband_addr(offset) & ~3; | |
83 | mask = (1 << ((offset >> 2) & 31)); | |
84 | mask = tswap32(mask); | |
85 | cpu_physical_memory_read(addr, (uint8_t *)&v, 4); | |
86 | return (v & mask) != 0; | |
87 | } | |
88 | ||
89 | static void bitband_writel(void *opaque, target_phys_addr_t offset, | |
90 | uint32_t value) | |
91 | { | |
92 | uint32_t addr; | |
93 | uint32_t mask; | |
94 | uint32_t v; | |
95 | addr = bitband_addr(offset) & ~3; | |
96 | mask = (1 << ((offset >> 2) & 31)); | |
97 | mask = tswap32(mask); | |
98 | cpu_physical_memory_read(addr, (uint8_t *)&v, 4); | |
99 | if (value & 1) | |
100 | v |= mask; | |
101 | else | |
102 | v &= ~mask; | |
103 | cpu_physical_memory_write(addr, (uint8_t *)&v, 4); | |
104 | } | |
105 | ||
106 | static CPUReadMemoryFunc *bitband_readfn[] = { | |
107 | bitband_readb, | |
108 | bitband_readw, | |
109 | bitband_readl | |
110 | }; | |
111 | ||
112 | static CPUWriteMemoryFunc *bitband_writefn[] = { | |
113 | bitband_writeb, | |
114 | bitband_writew, | |
115 | bitband_writel | |
116 | }; | |
117 | ||
118 | static void armv7m_bitband_init(void) | |
119 | { | |
120 | int iomemtype; | |
121 | ||
122 | iomemtype = cpu_register_io_memory(0, bitband_readfn, bitband_writefn, | |
123 | NULL); | |
124 | cpu_register_physical_memory(0x22000000, 0x02000000, iomemtype); | |
125 | cpu_register_physical_memory(0x42000000, 0x02000000, iomemtype); | |
126 | } | |
127 | ||
128 | /* Board init. */ | |
129 | /* Init CPU and memory for a v7-M based board. | |
130 | flash_size and sram_size are in kb. | |
131 | Returns the NVIC array. */ | |
132 | ||
133 | qemu_irq *armv7m_init(int flash_size, int sram_size, | |
134 | const char *kernel_filename, const char *cpu_model) | |
135 | { | |
136 | CPUState *env; | |
137 | qemu_irq *pic; | |
138 | uint32_t pc; | |
139 | int image_size; | |
140 | uint64_t entry; | |
141 | uint64_t lowaddr; | |
142 | ||
143 | flash_size *= 1024; | |
144 | sram_size *= 1024; | |
145 | ||
146 | if (!cpu_model) | |
147 | cpu_model = "cortex-m3"; | |
148 | env = cpu_init(cpu_model); | |
149 | if (!env) { | |
150 | fprintf(stderr, "Unable to find CPU definition\n"); | |
151 | exit(1); | |
152 | } | |
153 | ||
154 | #if 0 | |
155 | /* > 32Mb SRAM gets complicated because it overlaps the bitband area. | |
156 | We don't have proper commandline options, so allocate half of memory | |
157 | as SRAM, up to a maximum of 32Mb, and the rest as code. */ | |
158 | if (ram_size > (512 + 32) * 1024 * 1024) | |
159 | ram_size = (512 + 32) * 1024 * 1024; | |
160 | sram_size = (ram_size / 2) & TARGET_PAGE_MASK; | |
161 | if (sram_size > 32 * 1024 * 1024) | |
162 | sram_size = 32 * 1024 * 1024; | |
163 | code_size = ram_size - sram_size; | |
164 | #endif | |
165 | ||
166 | /* Flash programming is done via the SCU, so pretend it is ROM. */ | |
167 | cpu_register_physical_memory(0, flash_size, IO_MEM_ROM); | |
168 | cpu_register_physical_memory(0x20000000, sram_size, | |
169 | flash_size + IO_MEM_RAM); | |
170 | armv7m_bitband_init(); | |
171 | ||
172 | pic = armv7m_nvic_init(env); | |
173 | ||
174 | image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL); | |
175 | if (image_size < 0) { | |
176 | image_size = load_image(kernel_filename, phys_ram_base); | |
177 | lowaddr = 0; | |
178 | } | |
179 | if (image_size < 0) { | |
180 | fprintf(stderr, "qemu: could not load kernel '%s'\n", | |
181 | kernel_filename); | |
182 | exit(1); | |
183 | } | |
184 | ||
185 | /* If the image was loaded at address zero then assume it is a | |
186 | regular ROM image and perform the normal CPU reset sequence. | |
187 | Otherwise jump directly to the entry point. */ | |
188 | if (lowaddr == 0) { | |
189 | env->regs[13] = tswap32(*(uint32_t *)phys_ram_base); | |
190 | pc = tswap32(*(uint32_t *)(phys_ram_base + 4)); | |
191 | } else { | |
192 | pc = entry; | |
193 | } | |
194 | env->thumb = pc & 1; | |
195 | env->regs[15] = pc & ~1; | |
196 | ||
197 | /* Hack to map an additional page of ram at the top of the address | |
198 | space. This stops qemu complaining about executing code outside RAM | |
199 | when returning from an exception. */ | |
200 | cpu_register_physical_memory(0xfffff000, 0x1000, IO_MEM_RAM + ram_size); | |
201 | ||
202 | return pic; | |
203 | } | |
204 |