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