Files
embbed-rust/book/start/hardware.html
2020-05-26 20:19:48 +08:00

457 lines
26 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>硬件 - The Embedded Rust Book</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="../highlight.css">
<link rel="stylesheet" href="../tomorrow-night.css">
<link rel="stylesheet" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded "><a href="../intro/index.html"><strong aria-hidden="true">1.</strong> 简介</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../intro/hardware.html"><strong aria-hidden="true">1.1.</strong> 硬件</a></li><li class="chapter-item expanded "><a href="../intro/no-std.html"><strong aria-hidden="true">1.2.</strong> no_std</a></li><li class="chapter-item expanded "><a href="../intro/tooling.html"><strong aria-hidden="true">1.3.</strong> 工具</a></li><li class="chapter-item expanded "><a href="../intro/install.html"><strong aria-hidden="true">1.4.</strong> 安装</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../intro/install/linux.html"><strong aria-hidden="true">1.4.1.</strong> Linux</a></li><li class="chapter-item expanded "><a href="../intro/install/macos.html"><strong aria-hidden="true">1.4.2.</strong> MacOS</a></li><li class="chapter-item expanded "><a href="../intro/install/windows.html"><strong aria-hidden="true">1.4.3.</strong> Windows</a></li><li class="chapter-item expanded "><a href="../intro/install/verify.html"><strong aria-hidden="true">1.4.4.</strong> 验证安装</a></li></ol></li></ol></li><li class="chapter-item expanded "><a href="../start/index.html"><strong aria-hidden="true">2.</strong> 入门</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../start/qemu.html"><strong aria-hidden="true">2.1.</strong> QEMU</a></li><li class="chapter-item expanded "><a href="../start/hardware.html" class="active"><strong aria-hidden="true">2.2.</strong> 硬件</a></li><li class="chapter-item expanded "><a href="../start/registers.html"><strong aria-hidden="true">2.3.</strong> 内存映射寄存器</a></li><li class="chapter-item expanded "><a href="../start/semihosting.html"><strong aria-hidden="true">2.4.</strong> 半主机</a></li><li class="chapter-item expanded "><a href="../start/panicking.html"><strong aria-hidden="true">2.5.</strong> 恐慌</a></li><li class="chapter-item expanded "><a href="../start/exceptions.html"><strong aria-hidden="true">2.6.</strong> 异常</a></li><li class="chapter-item expanded "><a href="../start/interrupts.html"><strong aria-hidden="true">2.7.</strong> 中断</a></li><li class="chapter-item expanded "><a href="../start/io.html"><strong aria-hidden="true">2.8.</strong> IO</a></li></ol></li><li class="chapter-item expanded "><a href="../peripherals/index.html"><strong aria-hidden="true">3.</strong> 外设</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../peripherals/a-first-attempt.html"><strong aria-hidden="true">3.1.</strong> 初试Rust</a></li><li class="chapter-item expanded "><a href="../peripherals/borrowck.html"><strong aria-hidden="true">3.2.</strong> 借用检查器</a></li><li class="chapter-item expanded "><a href="../peripherals/singletons.html"><strong aria-hidden="true">3.3.</strong> 单例</a></li></ol></li><li class="chapter-item expanded "><a href="../static-guarantees/index.html"><strong aria-hidden="true">4.</strong> 静态保证</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../static-guarantees/typestate-programming.html"><strong aria-hidden="true">4.1.</strong> 类型状态机编程</a></li><li class="chapter-item expanded "><a href="../static-guarantees/state-machines.html"><strong aria-hidden="true">4.2.</strong> 外设作为状态机</a></li><li class="chapter-item expanded "><a href="../static-guarantees/design-contracts.html"><strong aria-hidden="true">4.3.</strong> 设计合约</a></li><li class="chapter-item expanded "><a href="../static-guarantees/zero-cost-abstractions.html"><strong aria-hidden="true">4.4.</strong> 零成本抽象</a></li></ol></li><li class="chapter-item expanded "><a href="../portability/index.html"><strong aria-hidden="true">5.</strong> 可移植性</a></li><li class="chapter-item expanded "><a href="../concurrency/index.html"><strong aria-hidden="true">6.</strong> 并发</a></li><li class="chapter-item expanded "><a href="../collections/index.html"><strong aria-hidden="true">7.</strong> 容器</a></li><li class="chapter-item expanded "><a href="../c-tips/index.html"><strong aria-hidden="true">8.</strong> 嵌入式C开发人员的技巧</a></li><li class="chapter-item expanded "><a href="../interoperability/index.html"><strong aria-hidden="true">9.</strong> 互操作性</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../interoperability/c-with-rust.html"><strong aria-hidden="true">9.1.</strong> Rust中使用C代码</a></li><li class="chapter-item expanded "><a href="../interoperability/rust-with-c.html"><strong aria-hidden="true">9.2.</strong> C中使用Rust代码</a></li></ol></li><li class="chapter-item expanded "><a href="../unsorted/index.html"><strong aria-hidden="true">10.</strong> 其他主题</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../unsorted/speed-vs-size.html"><strong aria-hidden="true">10.1.</strong> 优化:速度大小的权衡</a></li><li class="spacer"></li></ol></li><li class="chapter-item expanded "><a href="../appendix/glossary.html">Appendix A: Glossary</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">The Embedded Rust Book</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1><a class="header" href="#硬件" id="硬件">硬件</a></h1>
<p>现在,您应该对工具和开发过程有所了解。在本节中,我们将切换到实际硬件,该过程将基本保持不变,让我们开始吧。</p>
<h2><a class="header" href="#了解您的硬件" id="了解您的硬件">了解您的硬件</a></h2>
<p>在我们开始之前,您需要确定目标设备的一些特征,因为这些特征将用于配置项目:</p>
<ul>
<li>
<p>ARM内核。例如Cortex-M3。</p>
</li>
<li>
<p>ARM内核是否包括FPU Cortex-M4<strong>F</strong>和Cortex-M7<strong>F</strong>内核都有FPU。</p>
</li>
<li>
<p>目标设备有多少闪存和RAM例如256 KiB的闪存和32 KiB的RAM。</p>
</li>
<li>
<p>闪存和RAM映射的地址空间在哪里例如RAM通常位于地址“0x2000_0000”。</p>
</li>
</ul>
<p>通常您可以在数据手册或设备的参考手册中找到这些信息。</p>
<p>在本节中我们将使用我们的参考硬件STM32F3DISCOVERY。该开发板包含STM32F303VCT6微控制器。该微控制器具有</p>
<ul>
<li>
<p>一个Cortex-M4F内核其中包括一个单精度FPU</p>
</li>
<li>
<p>闪存的256 KiB位于地址0x0800_0000。</p>
</li>
<li>
<p>位于地址0x2000_0000的40KiBRAM。 (还有另一个RAM区域为简单起见我们将其忽略)。</p>
</li>
</ul>
<h2><a class="header" href="#配置" id="配置">配置</a></h2>
<p>我们将从一个新的模板实例开始。如果没有<code>cargo-generate</code>工具,请参阅<a href="qemu.html">上一小节的QEMU</a></p>
<pre><code class="language-console">$ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
Project Name: app
Creating project called `app`...
Done! New project created /tmp/app
$ cd app
</code></pre>
<p>第一个步是在.cargo/config中设置默认的编译目标。</p>
<pre><code class="language-console">$ tail -n5 .cargo/config
</code></pre>
<pre><code class="language-toml"># Pick ONE of these compilation targets
# target = &quot;thumbv6m-none-eabi&quot; # Cortex-M0 and Cortex-M0+
# target = &quot;thumbv7m-none-eabi&quot; # Cortex-M3
# target = &quot;thumbv7em-none-eabi&quot; # Cortex-M4 and Cortex-M7 (no FPU)
target = &quot;thumbv7em-none-eabihf&quot; # Cortex-M4F and Cortex-M7F (with FPU)
</code></pre>
<p>这次用得是Cortex-M4F内核,所以target使用<code>thumbv7em-none-eabihf</code></p>
<p>第二步是将存储区域信息输入到“memory.x”文件中。</p>
<pre><code class="language-console">$ cat memory.x
/* Linker script for the STM32F303VCT6 */
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
FLASH : ORIGIN = 0x08000000, LENGTH = 256K
RAM : ORIGIN = 0x20000000, LENGTH = 40K
}
</code></pre>
<p>确保<code>debug::exit()</code>调用已被注释掉或删除因为他仅用于QEMU环境。</p>
<pre><code class="language-rust ignore">#[entry]
fn main() -&gt; ! {
hprintln!(&quot;Hello, world!&quot;).unwrap();
// exit QEMU
// NOTE do not run this on hardware; it can corrupt OpenOCD state
// debug::exit(debug::EXIT_SUCCESS);
loop {}
}
</code></pre>
<p>现在,您可以像以前一样使用<code>cargo build</code>交叉编译程序,并使用<code>cargo-binutils</code>检查二进制文件。 <code>cortex-m-rt</code> crate可处理让您的芯片运行所需的所有魔术,几乎所有Cortex-M CPU都以相同的方式引导。</p>
<pre><code class="language-console">$ cargo build --example hello
</code></pre>
<h2><a class="header" href="#调试" id="调试">调试</a></h2>
<p>调试看起来会有所不同。实际上根据目标设备的不同第一步看起来可能会有所不同。在本节中我们将介绍在STM32F3DISCOVERY上调试程序所需的步骤。有关设备的特定信息请查看<a href="https://github.com/rust-embedded/debugonomicon">Debugonomicon</a></p>
<p>和以前一样我们将进行远程调试客户端是GDB进程,服务器将是OpenOCD。</p>
<p>$ cat openocd.cfg
按照<a href="../intro/install/verify.html">验证</a>部分的操作将开发板连接到笔记本电脑或者PC并检查是否插上了ST-LINK跳线帽。</p>
<p>在终端上从模板的根目录运行“openocd”以连接到开发板上的ST-LINK。 <code>openocd</code>会根据<code>openocd.cfg</code>文件,找到要使用的接口文件和目标文件。</p>
<pre><code class="language-console">$ cat openocd.cfg
</code></pre>
<pre><code class="language-text"># Sample OpenOCD configuration for the STM32F3DISCOVERY development board
# Depending on the hardware revision you got you'll have to pick ONE of these
# interfaces. At any time only one interface should be commented out.
# Revision C (newer revision)
source [find interface/stlink-v2-1.cfg]
# Revision A and B (older revisions)
# source [find interface/stlink-v2.cfg]
source [find target/stm32f3x.cfg]
</code></pre>
<blockquote>
<p><strong>注意</strong>如果您在<a href="../intro/install/verify.html">验证</a>部分发现开发板的版本较旧,则此时应修改<code>openocd.cfg</code>文件以使用<code>interface/stlink-v2.cfg</code></p>
</blockquote>
<pre><code class="language-console">$ openocd
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport &quot;hla_swd&quot;. To override use 'transport select &lt;transport&gt;'.
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 2.913879
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
</code></pre>
<p>在另一个终端上也从模板的根目录运行GDB。</p>
<pre><code class="language-console">$ &lt;gdb&gt; -q target/thumbv7em-none-eabihf/debug/examples/hello
</code></pre>
<p>接下来将GDB连接到OpenOCDOpenOCD正在监听端口3333。</p>
<pre><code class="language-console">(gdb) target remote :3333
Remote debugging using :3333
0x00000000 in ?? ()
</code></pre>
<p>现在,使用<code>load</code>命令将程序加载到微控制器上。</p>
<pre><code class="language-console">(gdb) load
Loading section .vector_table, size 0x400 lma 0x8000000
Loading section .text, size 0x1e70 lma 0x8000400
Loading section .rodata, size 0x61c lma 0x8002270
Start address 0x800144e, load size 10380
Transfer rate: 17 KB/sec, 3460 bytes/write.
</code></pre>
<p>现在程序已加载。该程序需要半主机支持因此在进行任何半主机调用之前我们必须告诉OpenOCD启用半主机。您可以使用“monitor”将命令发送到OpenOCD。</p>
<pre><code class="language-console">(gdb) monitor arm semihosting enable
semihosting is enabled
</code></pre>
<blockquote>
<p>您可以通过调用<code>monitor help</code>命令来查看所有OpenOCD命令。</p>
</blockquote>
<p>像之前一样,我们可以使用断点和<code>continue</code>跳过所有跳转到<code>main</code>函数。</p>
<pre><code class="language-console">(gdb) break main
Breakpoint 1 at 0x8000d18: file examples/hello.rs, line 15.
(gdb) continue
Continuing.
Note: automatically using hardware breakpoints for read-only addresses.
Breakpoint 1, main () at examples/hello.rs:15
15 let mut stdout = hio::hstdout().unwrap();
</code></pre>
<blockquote>
<p><strong>注意</strong>如果执行<code>continue</code>命令后GDB阻塞了终端而不是停在了断点上则可能需要仔细检查<code>memory.x</code>文件中的内存区域信息是否配置正确(起始地址和长度)。</p>
</blockquote>
<p><code>next</code>命令替代刚刚的<code>continue</code>,应该也会产生相同的结果。</p>
<pre><code class="language-console">(gdb) next
16 writeln!(stdout, &quot;Hello, world!&quot;).unwrap();
(gdb) next
19 debug::exit(debug::EXIT_SUCCESS);
</code></pre>
<p>此时,您应该看到&quot;Hello, world!&quot; 打印在OpenOCD控制台上等等。</p>
<pre><code class="language-console">$ openocd
(..)
Info : halted: PC: 0x08000e6c
Hello, world!
Info : halted: PC: 0x08000d62
Info : halted: PC: 0x08000d64
Info : halted: PC: 0x08000d66
Info : halted: PC: 0x08000d6a
Info : halted: PC: 0x08000a0c
Info : halted: PC: 0x08000d70
Info : halted: PC: 0x08000d72
</code></pre>
<p>发出另一个<code>next</code>将使处理器执行<code>debug::exit</code>。这会像断点一样挂起程序的执行:</p>
<pre><code class="language-console">(gdb) next
Program received signal SIGTRAP, Trace/breakpoint trap.
0x0800141a in __syscall ()
</code></pre>
<p>OpenOCD控制台将会打印如下内容</p>
<pre><code class="language-console">$ openocd
(..)
Info : halted: PC: 0x08001188
semihosting: *** application exited ***
Warn : target not halted
Warn : target not halted
target halted due to breakpoint, current mode: Thread
xPSR: 0x21000000 pc: 0x08000d76 msp: 0x20009fc0, semihosting
</code></pre>
<p>但是,在微控制器上运行的程序尚未终止,您可以使用<code>continue</code>或类似命令将其恢复。</p>
<p>现在,您可以使用“ quit”命令退出GDB。</p>
<pre><code class="language-console">(gdb) quit
</code></pre>
<p>现在调试需要更多步骤,因此我们将所有这些步骤打包到一个名为<code>openocd.gdb</code>的GDB脚本中。</p>
<pre><code class="language-console">$ cat openocd.gdb
</code></pre>
<pre><code class="language-text">target remote :3333
# print demangled symbols
set print asm-demangle on
# detect unhandled exceptions, hard faults and panics
break DefaultHandler
break HardFault
break rust_begin_unwind
monitor arm semihosting enable
load
# start the process but immediately halt the processor
stepi
</code></pre>
<p>现在运行 <code>&lt;gdb&gt; -x openocd.gdb $program</code>将立即将GDB连接到OpenOCD启用半主机加载程序并开始执行。</p>
<p>您也可以将<code>&lt;gdb&gt; -x openocd.gdb</code>转换为自定义运行器,这样<code>cargo run</code>会自动构建程序并开始GDB会话。该运行器已包含在<code>.cargo/config</code>中,只不过现在是被注释掉的状态。</p>
<pre><code class="language-console">$ head -n10 .cargo/config
</code></pre>
<pre><code class="language-toml">[target.thumbv7m-none-eabi]
# uncomment this to make `cargo run` execute programs on QEMU
# runner = &quot;qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel&quot;
[target.'cfg(all(target_arch = &quot;arm&quot;, target_os = &quot;none&quot;))']
# uncomment ONE of these three option to make `cargo run` start a GDB session
# which option to pick depends on your system
runner = &quot;arm-none-eabi-gdb -x openocd.gdb&quot;
# runner = &quot;gdb-multiarch -x openocd.gdb&quot;
# runner = &quot;gdb -x openocd.gdb&quot;
</code></pre>
<pre><code class="language-console">$ cargo run --example hello
(..)
Loading section .vector_table, size 0x400 lma 0x8000000
Loading section .text, size 0x1e70 lma 0x8000400
Loading section .rodata, size 0x61c lma 0x8002270
Start address 0x800144e, load size 10380
Transfer rate: 17 KB/sec, 3460 bytes/write.
(gdb)
</code></pre>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../start/qemu.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="../start/registers.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../start/qemu.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="../start/registers.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script type="text/javascript">
window.playpen_copyable = true;
</script>
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
</body>
</html>