]>
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 | ||
87ecb68b PB |
10 | #include "hw.h" |
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 | ||
120 | static void armv7m_bitband_init(void) | |
121 | { | |
122 | int iomemtype; | |
8da3ff18 PB |
123 | static uint32_t bitband1_offset = 0x20000000; |
124 | static uint32_t bitband2_offset = 0x40000000; | |
9ee6e8bb PB |
125 | |
126 | iomemtype = cpu_register_io_memory(0, bitband_readfn, bitband_writefn, | |
8da3ff18 | 127 | &bitband1_offset); |
9ee6e8bb | 128 | cpu_register_physical_memory(0x22000000, 0x02000000, iomemtype); |
8da3ff18 PB |
129 | iomemtype = cpu_register_io_memory(0, bitband_readfn, bitband_writefn, |
130 | &bitband2_offset); | |
9ee6e8bb PB |
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. */ | |
dcac9679 PB |
173 | cpu_register_physical_memory(0, flash_size, |
174 | qemu_ram_alloc(flash_size) | IO_MEM_ROM); | |
9ee6e8bb | 175 | cpu_register_physical_memory(0x20000000, sram_size, |
dcac9679 | 176 | qemu_ram_alloc(sram_size) | IO_MEM_RAM); |
9ee6e8bb PB |
177 | armv7m_bitband_init(); |
178 | ||
179 | pic = armv7m_nvic_init(env); | |
180 | ||
181 | image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL); | |
182 | if (image_size < 0) { | |
dcac9679 | 183 | image_size = load_image_targphys(kernel_filename, 0, flash_size); |
9ee6e8bb PB |
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] = tswap32(*(uint32_t *)phys_ram_base); | |
197 | pc = tswap32(*(uint32_t *)(phys_ram_base + 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. */ | |
dcac9679 PB |
207 | cpu_register_physical_memory(0xfffff000, 0x1000, |
208 | qemu_ram_alloc(0x1000) | IO_MEM_RAM); | |
9ee6e8bb PB |
209 | |
210 | return pic; | |
211 | } |