Add README for tutorial 10_DMA_memory

pull/30/head
Andre Richter 5 years ago
parent c07413e2eb
commit 9818779ff2
No known key found for this signature in database
GPG Key ID: 2116C1AB102F615E

@ -1,13 +1,245 @@
# Tutorial 10 - DMA Memory
Coming soon!
There's a secret I haven't told you! A certain part of our code doesn't work
anymore since the [virtual memory](../0D_virtual_memory) tutorial. There is a
regression that manifests in the `Videocore Mailbox` driver. It will only work
until **paging and caching** is switched on. Afterwards, the `call()` method
will fail. Why is that?
This lesson will teach about:
- Simple bump memory allocators and non-cacheable memory.
- Using MiniUart for early boot messages and dynamically switching to the PL011
Uart later (which now needs the memory allocator that theoretically could fail
- which the MiniUart could then print).
The reason is that in our code, the RPi's processor is sharing a `DRAM buffer`
with the `Videocore` device. In other words, the concept of **shared memory** is
used. Let's recall a simplified version of the protocol:
1. RPi `CPU` checks the `STATUS` MMIO register of the `Videcore` if a message can
be written.
2. If so, `CPU` writes the address of the `DRAM buffer` in which the actual
message is stored into the `Videocore`'s `WRITE` MMIO register.
3. `CPU` checks the `STATUS` and `READ` MMIO registers if the Videocore has
answered.
4. If so, `CPU` checks the first `u32` word of the earlier provided `DRAM buffer`
if the response is valid (the `Videocore` puts its answer into the same buffer
in which the original request was stored. This is what is commonly called
a `DMA` transaction).
At step **4**, things break. The reason is that code and **page tables** were
set up in a way that the `DRAM buffer` used for message exchange between `CPU`
and Videcore is attributed as _cacheable_.
So when the `CPU` is writing to the buffer, the contents might not get written
back to `DRAM` in time before the notification of a new message is signaled to
the Videocore via the `WRITE` MMIO register (which is correctly attributed as
device memory in the page tables and hence not cached).
Even if the contents would land in `DRAM` in time, the `Videocore`'s answer
which overwrites the same buffer would not be reflected in the `CPU`'s cache,
since there is no coherency mechanism in place between the two. The RPi `CPU`
would read back the same values it put into the buffer itself when setting up
the message, and not the `DRAM` content that contains the answer.
![DMA block diagram](../doc/dma_0.png)
The regression did not manifest yet because the Mailbox is only used before the
paging caching is switched on, and never afterwards. However, now is a good time
to fix this.
## An Allocator for DMA Memory
The first step is to introduce a region of _non-cacheable DRAM_ in the
`KERNEL_VIRTUAL_LAYOUT` in `memory.rs`:
```rust
Descriptor {
name: "DMA heap pool",
virtual_range: || RangeInclusive::new(map::virt::DMA_HEAP_START, map::virt::DMA_HEAP_END),
translation: Translation::Identity,
attribute_fields: AttributeFields {
mem_attributes: MemAttributes::NonCacheableDRAM,
acc_perms: AccessPermissions::ReadWrite,
execute_never: true,
},
},
```
When you saw the inferior performance of non-cacheable mapped DRAM compared to
cacheable DRAM in the [cache performance tutorial](../0E_cache_performance)
earlier and asked yourself why anybody would ever want this: Exactly for the
use-case at hand!
Theoretically, some linker hacks could be used to ensure that the `Videcore` is
using a buffer that is statically linked to the DMA heap pool once paging and
caching is turned on. However, in real-world kernels, it is common to frequently
map/allocate and unmap/free chunks of `DMA` memory at runtime, for example in
device drivers for DMA-capable devices.
Hence, let's introduce an `allocator`.
### Bump Allocation
As always in the tutorials, a simple implementation is used for getting started
with basic concepts of a topic, and upgrades are introduced when they are
needed.
In a `bump allocator`, when a requests comes in, it always returns the next
possible aligned region of its heap until it runs out of memory. What makes it
really simple is that it doesn't provide means for freeing memory again. When no
more memory is left, game is over.
Conveniently enough, [Rust already provides memory allocation APIs](https://doc.rust-lang.org/alloc/alloc/index.html). There is an
[Alloc](https://doc.rust-lang.org/alloc/alloc/trait.Alloc.html) and a
[GlobalAlloc](https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html)
trait. The latter is intended for realizing a _default allocator_, meaning it
would be the allocator used for any standard language construtcs that
automatically allocate something on the heap, for example a
[Box](https://doc.rust-lang.org/alloc/boxed/index.html). There can only be one
global allocator, so the tutorials will make use of it for cacheable DRAM later.
Hence, for the DMA bump allocator,
[Alloc](https://doc.rust-lang.org/alloc/alloc/trait.Alloc.html) will be
used. What is also really nice is that for both traits, only the `alloc()`
method needs to be implemented. If this is done, you automatically get a bunch
of additional default methods for free, e.g. `alloc_zeroed()`.
Here is the implementation in `memory/bump_allocator.rs`:
```rust
pub struct BumpAllocator {
next: usize,
pool_end: usize,
name: &'static str,
}
unsafe impl Alloc for BumpAllocator {
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
let start = crate::memory::aligned_addr_unchecked(self.next, layout.align());
let end = start + layout.size();
if end <= self.pool_end {
self.next = end;
println!(
"[i] {}:\n Allocated Addr {:#010X} Size {:#X}",
self.name,
start,
layout.size()
);
Ok(NonNull::new_unchecked(start as *mut u8))
} else {
Err(AllocErr)
}
}
// A bump allocator doesn't care
unsafe fn dealloc(&mut self, _ptr: NonNull<u8>, _layout: Layout) {}
}
```
The `alloc()` method returns a pointer to memory. However, it is safer to
operate with [slices](https://doc.rust-lang.org/alloc/slice/index.html), since
they are intrinsically bounds-checked. Therefore, the `BumpAllocator` gets an
additional method called `alloc_slice_zeroed()`, which wraps around
`alloc_zeroed()` provided by the `Alloc` trait and on success returns a `&'a mut
[T]`.
### Global Instance
A global instance of the allocator is needed, and since its methods demand
_mutable references_ to `self`, it is wrapped into a `NullLock`, which was
introduced in the [last tutorial](../0F_globals_synchronization_println):
```rust
/// The global allocator for DMA-able memory. That is, memory which is tagged
/// non-cacheable in the page tables.
static DMA_ALLOCATOR: sync::NullLock<memory::BumpAllocator> =
sync::NullLock::new(memory::BumpAllocator::new(
memory::map::virt::DMA_HEAP_START as usize,
memory::map::virt::DMA_HEAP_END as usize,
"Global DMA Allocator",
));
```
## Videocore Driver
The `Videocore` driver has to be changed to use the allocator during
instantiation, and in contrast to earlier, this could fail now:
```rust
let ret = crate::DMA_ALLOCATOR.lock(|d| d.alloc_slice_zeroed(MBOX_SIZE, MBOX_ALIGNMENT));
if ret.is_err() {
return Err(());
}
```
## Reorg of the Kernel Init
Since the `Videcore` now depends on the `DMA Allocator`, its initialization must
now happen _after_ the `MMU init`, which turns on **paging and caching**. This,
in turn, means that the `PL011 UART`, which is used for printing and needs the
`Videcore` for its setup, has to shift its init as well. So there is a lot of
shuffling happening.
In summary, the new init procedure would be:
1. GPIO
2. MMU
3. Videcore
4. PL011 UART
That is a bit unfortunate, because if anything goes wrong at `MMU` or
`Videocore` init, we can not print any fault info on the console. For this
reason, the `MiniUart` from the earlier tutorials is revived, because it only
needs the `GPIO` driver to set itself up. So here is the revamped init:
1. GPIO
2. MiniUart
3. MMU
4. Videcore
5. PL011 UART
Using this procedure, the `MiniUart` can report faults for any of the subsequent
stages like`MMU` or `Videocore` init. If all is successful and the more capable
`PL011 UART` comes online, we can let it conveniently replace the `MiniUart`
through the `CONSOLE.replace_with()` scheme introduced in the [last tutorial](../0F_globals_synchronization_println).
### Make it Fault
If you feel curious and want to put all the theory to action, take a look at the
code in `main.rs` for the DMA allocator instantiation and try the changes in the
comments:
```rust
/// The global allocator for DMA-able memory. That is, memory which is tagged
/// non-cacheable in the page tables.
static DMA_ALLOCATOR: sync::NullLock<memory::BumpAllocator> =
sync::NullLock::new(memory::BumpAllocator::new(
memory::map::virt::DMA_HEAP_START as usize,
memory::map::virt::DMA_HEAP_END as usize,
"Global DMA Allocator",
// Try the following arguments instead to see the PL011 UART init
// fail. It will cause the allocator to use memory that is marked
// cacheable and therefore not DMA-safe. The communication with the
// Videocore will therefore fail.
// 0x00600000 as usize,
// 0x007FFFFF as usize,
// "Global Non-DMA Allocator",
));
```
This might only work on the real HW and not in QEMU.
## QEMU
On the actual HW it is possible to reprogram the same `GPIO` pins at runtime to
either use the `MiniUart` or the `PL011`, and as a result the console output of
both is sent through the same USB-serial dongle. This is transparent to the
user.
On QEMU, unfortunately, two different virtual terminals must be used and this
multiplexing is not possible. As a result, you'll see that the QEMU output has
changed in optics a bit and now provides separate views for the two `UARTs`.
## Output

@ -49,10 +49,9 @@ static DMA_ALLOCATOR: sync::NullLock<memory::BumpAllocator> =
memory::map::virt::DMA_HEAP_END as usize,
"Global DMA Allocator",
// Try the following arguments instead to see the PL011 UART init
// fail. It will cause the allocator to use memory that are marked
// cacheable and therefore not DMA-safe. The answer from the Videocore
// won't be received by the CPU because it reads an old cached value
// that resembles an error case instead.
// fail. It will cause the allocator to use memory that is marked
// cacheable and therefore not DMA-safe. The communication with the
// Videocore will therefore fail.
// 0x00600000 as usize,
// 0x007FFFFF as usize,

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

@ -0,0 +1,402 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="870"
height="180"
viewBox="0 0 230.18749 47.625001"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="dma_0.svg"
inkscape:export-xdpi="95.999992"
inkscape:export-ydpi="95.999992">
<defs
id="defs2">
<marker
inkscape:stockid="Arrow2Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path2173"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(1.1,0,0,1.1,1.1,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path2176"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker1253"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path1251"
style="fill:#ff6600;fill-opacity:1;fill-rule:evenodd;stroke:#ff6600;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker1193"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#cc0000;fill-opacity:1;fill-rule:evenodd;stroke:#cc0000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path1191"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker1768"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#669900;fill-opacity:1;fill-rule:evenodd;stroke:#669900;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path1766"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker1415"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path1413"
style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:#808080;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lstart-5"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path2173-4"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(1.1,0,0,1.1,1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-6"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path2176-8"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lstart-5-5"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path2173-4-5"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(1.1,0,0,1.1,1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-6-6"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path2176-8-3"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.99999999"
inkscape:cx="233.75149"
inkscape:cy="343.57437"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
showguides="true"
inkscape:snap-grids="true"
inkscape:snap-text-baseline="true"
inkscape:window-width="1920"
inkscape:window-height="1172"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid10" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-249.37499)">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="103.18751"
y="56.229153"
id="text4524-3"><tspan
sodipodi:role="line"
id="tspan4522-6"
x="103.18751"
y="66.100143"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono';stroke-width:0.26458332" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;line-height:4px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="96.572922"
y="254.66664"
id="text4524-6-0-4-2-1-02-1-0-8-2-0-69-7-9-2"><tspan
sodipodi:role="line"
x="96.572922"
y="257.75211"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;line-height:4px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono';text-align:center;text-anchor:middle;stroke-width:0.26458332"
id="tspan2059-6-3-70" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="63.715191"
y="268.32797"
id="text2086"><tspan
sodipodi:role="line"
id="tspan2084"
x="63.715191"
y="268.32797"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono';stroke-width:0.26458332">core0</tspan></text>
<rect
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.26458332;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect2088"
width="13.229166"
height="13.229166"
x="61.515621"
y="260.61978" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#bfbfbf;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="80.913109"
y="268.32797"
id="text2086-3"><tspan
sodipodi:role="line"
id="tspan2084-0"
x="80.913109"
y="268.32797"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono';fill:#bfbfbf;stroke-width:0.26458332">core1</tspan></text>
<rect
style="fill:#000000;fill-opacity:0;stroke:#bfbfbf;stroke-width:0.26458332;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect2088-2"
width="13.229167"
height="13.229167"
x="78.713539"
y="260.61978" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#bfbfbf;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="63.715191"
y="284.20297"
id="text2086-0"><tspan
sodipodi:role="line"
id="tspan2084-9"
x="63.715191"
y="284.20297"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono';fill:#bfbfbf;stroke-width:0.26458332">core2</tspan></text>
<rect
style="fill:#000000;fill-opacity:0;stroke:#bfbfbf;stroke-width:0.26458332;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect2088-4"
width="13.229167"
height="13.229167"
x="61.515621"
y="276.49478" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#bfbfbf;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="80.913109"
y="284.20297"
id="text2086-9"><tspan
sodipodi:role="line"
id="tspan2084-3"
x="80.913109"
y="284.20297"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono';fill:#bfbfbf;stroke-width:0.26458332">core3</tspan></text>
<rect
style="fill:#000000;fill-opacity:0;stroke:#bfbfbf;stroke-width:0.26458332;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect2088-8"
width="13.229167"
height="13.229167"
x="78.713539"
y="276.49478" />
<rect
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.26458332;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect2131"
width="50.270832"
height="38.364582"
x="58.869789"
y="254.00519" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="96.13018"
y="276.37131"
id="text2086-37"><tspan
sodipodi:role="line"
id="tspan2084-1"
x="96.13018"
y="276.37131"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono';stroke-width:0.26458332">Cache</tspan></text>
<rect
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.26458332;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect2151"
width="11.90625"
height="29.104166"
x="94.588539"
y="260.61978" />
<path
style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Lstart);marker-end:url(#Arrow2Lend)"
d="M 75.273958,271.2031 H 94.059374"
id="path2153"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="81.359375"
y="257.97394"
id="text2086-7"><tspan
sodipodi:role="line"
x="81.359375"
y="257.97394"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono';stroke-width:0.26458332"
id="tspan2681">CPU</tspan></text>
<rect
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.26458332;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect2685"
width="15.874998"
height="38.36459"
x="122.36979"
y="254.00519" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="126.7407"
y="274.2811"
id="text2086-37-1"><tspan
sodipodi:role="line"
id="tspan2084-1-8"
x="126.7407"
y="274.2811"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono';stroke-width:0.26458332">DRAM</tspan></text>
<rect
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.26458332;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect2685-4"
width="19.843758"
height="38.364578"
x="151.47395"
y="254.00519" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="153.51477"
y="274.39047"
id="text2086-37-1-3"><tspan
sodipodi:role="line"
id="tspan2084-1-8-2"
x="153.51477"
y="274.39047"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono';stroke-width:0.26458332">Videocore</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Lstart-5);marker-end:url(#Arrow2Lend-6)"
d="m 107.02396,273.18748 h 14.81667"
id="path2153-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Lstart-5-5);marker-end:url(#Arrow2Lend-6-6)"
d="m 138.77396,273.18747 h 12.17083"
id="path2153-1-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

Loading…
Cancel
Save