]>
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 | ||
fe7e8758 | 10 | #include "sysbus.h" |
87ecb68b PB |
11 | #include "arm-misc.h" |
12 | #include "sysemu.h" | |
9ee6e8bb PB |
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. */ | |
8da3ff18 | 17 | static inline uint32_t bitband_addr(void * opaque, uint32_t addr) |
9ee6e8bb PB |
18 | { |
19 | uint32_t res; | |
20 | ||
8da3ff18 | 21 | res = *(uint32_t *)opaque; |
9ee6e8bb PB |
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; | |
8da3ff18 | 30 | cpu_physical_memory_read(bitband_addr(opaque, offset), &v, 1); |
9ee6e8bb PB |
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; | |
8da3ff18 | 40 | addr = bitband_addr(opaque, offset); |
9ee6e8bb PB |
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; | |
8da3ff18 | 55 | addr = bitband_addr(opaque, offset) & ~1; |
9ee6e8bb PB |
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; | |
8da3ff18 | 68 | addr = bitband_addr(opaque, offset) & ~1; |
9ee6e8bb PB |
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; | |
8da3ff18 | 84 | addr = bitband_addr(opaque, offset) & ~3; |
9ee6e8bb PB |
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; | |
8da3ff18 | 97 | addr = bitband_addr(opaque, offset) & ~3; |
9ee6e8bb PB |
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 | ||
40905a6a PB |
120 | typedef struct { |
121 | SysBusDevice busdev; | |
122 | uint32_t base; | |
123 | } BitBandState; | |
124 | ||
125 | static void bitband_init(SysBusDevice *dev) | |
9ee6e8bb | 126 | { |
40905a6a | 127 | BitBandState *s = FROM_SYSBUS(BitBandState, dev); |
9ee6e8bb PB |
128 | int iomemtype; |
129 | ||
40905a6a | 130 | s->base = qdev_get_prop_int(&dev->qdev, "base", 0); |
9ee6e8bb | 131 | iomemtype = cpu_register_io_memory(0, bitband_readfn, bitband_writefn, |
40905a6a PB |
132 | &s->base); |
133 | sysbus_init_mmio(dev, 0x02000000, iomemtype); | |
134 | } | |
135 | ||
136 | static void armv7m_bitband_init(void) | |
137 | { | |
138 | DeviceState *dev; | |
139 | ||
140 | dev = qdev_create(NULL, "ARM,bitband-memory"); | |
141 | qdev_set_prop_int(dev, "base", 0x20000000); | |
142 | qdev_init(dev); | |
143 | sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x22000000); | |
144 | ||
145 | dev = qdev_create(NULL, "ARM,bitband-memory"); | |
146 | qdev_set_prop_int(dev, "base", 0x40000000); | |
147 | qdev_init(dev); | |
148 | sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x42000000); | |
9ee6e8bb PB |
149 | } |
150 | ||
151 | /* Board init. */ | |
152 | /* Init CPU and memory for a v7-M based board. | |
153 | flash_size and sram_size are in kb. | |
154 | Returns the NVIC array. */ | |
155 | ||
156 | qemu_irq *armv7m_init(int flash_size, int sram_size, | |
157 | const char *kernel_filename, const char *cpu_model) | |
158 | { | |
159 | CPUState *env; | |
fe7e8758 PB |
160 | DeviceState *nvic; |
161 | /* FIXME: make this local state. */ | |
162 | static qemu_irq pic[64]; | |
163 | qemu_irq *cpu_pic; | |
9ee6e8bb PB |
164 | uint32_t pc; |
165 | int image_size; | |
166 | uint64_t entry; | |
167 | uint64_t lowaddr; | |
fe7e8758 | 168 | int i; |
9ee6e8bb PB |
169 | |
170 | flash_size *= 1024; | |
171 | sram_size *= 1024; | |
172 | ||
173 | if (!cpu_model) | |
174 | cpu_model = "cortex-m3"; | |
175 | env = cpu_init(cpu_model); | |
176 | if (!env) { | |
177 | fprintf(stderr, "Unable to find CPU definition\n"); | |
178 | exit(1); | |
179 | } | |
180 | ||
181 | #if 0 | |
182 | /* > 32Mb SRAM gets complicated because it overlaps the bitband area. | |
183 | We don't have proper commandline options, so allocate half of memory | |
184 | as SRAM, up to a maximum of 32Mb, and the rest as code. */ | |
185 | if (ram_size > (512 + 32) * 1024 * 1024) | |
186 | ram_size = (512 + 32) * 1024 * 1024; | |
187 | sram_size = (ram_size / 2) & TARGET_PAGE_MASK; | |
188 | if (sram_size > 32 * 1024 * 1024) | |
189 | sram_size = 32 * 1024 * 1024; | |
190 | code_size = ram_size - sram_size; | |
191 | #endif | |
192 | ||
193 | /* Flash programming is done via the SCU, so pretend it is ROM. */ | |
dcac9679 PB |
194 | cpu_register_physical_memory(0, flash_size, |
195 | qemu_ram_alloc(flash_size) | IO_MEM_ROM); | |
9ee6e8bb | 196 | cpu_register_physical_memory(0x20000000, sram_size, |
dcac9679 | 197 | qemu_ram_alloc(sram_size) | IO_MEM_RAM); |
9ee6e8bb PB |
198 | armv7m_bitband_init(); |
199 | ||
fe7e8758 | 200 | nvic = qdev_create(NULL, "armv7m_nvic"); |
bdb11366 | 201 | env->v7m.nvic = nvic; |
fe7e8758 PB |
202 | qdev_init(nvic); |
203 | cpu_pic = arm_pic_init_cpu(env); | |
204 | sysbus_connect_irq(sysbus_from_qdev(nvic), 0, cpu_pic[ARM_PIC_CPU_IRQ]); | |
205 | for (i = 0; i < 64; i++) { | |
067a3ddc | 206 | pic[i] = qdev_get_gpio_in(nvic, i); |
fe7e8758 | 207 | } |
9ee6e8bb PB |
208 | |
209 | image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL); | |
210 | if (image_size < 0) { | |
dcac9679 | 211 | image_size = load_image_targphys(kernel_filename, 0, flash_size); |
9ee6e8bb PB |
212 | lowaddr = 0; |
213 | } | |
214 | if (image_size < 0) { | |
215 | fprintf(stderr, "qemu: could not load kernel '%s'\n", | |
216 | kernel_filename); | |
217 | exit(1); | |
218 | } | |
219 | ||
220 | /* If the image was loaded at address zero then assume it is a | |
221 | regular ROM image and perform the normal CPU reset sequence. | |
222 | Otherwise jump directly to the entry point. */ | |
223 | if (lowaddr == 0) { | |
44654490 PB |
224 | env->regs[13] = ldl_phys(0); |
225 | pc = ldl_phys(4); | |
9ee6e8bb PB |
226 | } else { |
227 | pc = entry; | |
228 | } | |
229 | env->thumb = pc & 1; | |
230 | env->regs[15] = pc & ~1; | |
231 | ||
232 | /* Hack to map an additional page of ram at the top of the address | |
233 | space. This stops qemu complaining about executing code outside RAM | |
234 | when returning from an exception. */ | |
dcac9679 PB |
235 | cpu_register_physical_memory(0xfffff000, 0x1000, |
236 | qemu_ram_alloc(0x1000) | IO_MEM_RAM); | |
9ee6e8bb PB |
237 | |
238 | return pic; | |
239 | } | |
40905a6a PB |
240 | |
241 | static void armv7m_register_devices(void) | |
242 | { | |
243 | sysbus_register_dev("ARM,bitband-memory", sizeof(BitBandState), | |
244 | bitband_init); | |
245 | } | |
246 | ||
247 | device_init(armv7m_register_devices) |