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