snaums.de - Betriebssystemhttps://www.snaums.de/2017-05-28T19:10:00+02:00PiOS: ARM Timer und Interrupts2017-05-28T19:10:00+02:002017-05-28T19:10:00+02:00snaumstag:www.snaums.de,2017-05-28:/informatik/pios-arm-timer-and-interrupts-de.html<p>Seit einiger Zeit arbeite ich an einem prototypischem Betriebssystem, PiOS ("früher" PilotOS) [1]. Ich habe früher schon einige Artikel über Prozesse [2][3] geschrieben und will nun hier den aktuellen Stand diskutieren. Neben einem Rewrite des Codes, also entfernen quasi aller alten Assembler-Sourcen und Neuschreiben der Funktionalität in C, sind …</p><p>Seit einiger Zeit arbeite ich an einem prototypischem Betriebssystem, PiOS ("früher" PilotOS) [1]. Ich habe früher schon einige Artikel über Prozesse [2][3] geschrieben und will nun hier den aktuellen Stand diskutieren. Neben einem Rewrite des Codes, also entfernen quasi aller alten Assembler-Sourcen und Neuschreiben der Funktionalität in C, sind auch Treiber entstanden für i2c und einem LCD character display, die aktuell aufgrund fehlender Testmöglichkeiten lediglich Versuchsballons sind. Weiter habe ich heute den ARM Timer [4, S. 196 ff] implementiert und meine ersten Interrupts erhalten.</p>
<p>Zunächst lege ich i.d.R. eine Map an für die Register, die im Speicheraddressraum eingebunden sind, sodass ich eine benannte Datenstruktur (i.d.R. ein <em>struct</em>) habe, welches ich benutzen kann um die Register der Peripherie anzusprechen (Stichwort: Memory Mapped I/O). Der ARM-Timer verfügt über 9 32-Bit Register:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="k">struct</span><span class="w"> </span><span class="nc">_pios_arm_timer_t</span></code></span>
<span class="code-line"><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">load</span><span class="p">;</span><span class="w"> </span><span class="c1">///< load a specific value into the counter, after counted-down an IRQ occurs</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">value</span><span class="p">;</span><span class="w"> </span><span class="c1">///< a value, counted down. On 0 IRQ is set and the value is reloaded from reload-register</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">control</span><span class="p">;</span><span class="w"> </span><span class="c1">///< control register, which allows for setting some settings</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">irqack</span><span class="p">;</span><span class="w"> </span><span class="c1">///< write only for clearing the IRQ-line</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">irqraw</span><span class="p">;</span><span class="w"> </span><span class="c1">///< is an interrupt pending? (i.e. LSB is set to 1)</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">irqmasked</span><span class="p">;</span><span class="w"> </span><span class="c1">///< is the IRQ pending bit set and the interrupt enable bit?</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">reload</span><span class="p">;</span><span class="w"> </span><span class="c1">///< copy of load, but writing will not trigger an overwrite of value, just when value reaches 0</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">prescale</span><span class="p">;</span><span class="w"> </span><span class="c1">///< setting a prescaler for the timer - LSB 10 Bits (timer_clock = apb_clock/(pre_divider+1))</span></span>
<span class="code-line"><span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">freerunning</span><span class="p">;</span><span class="w"> </span><span class="c1">///< a free running value (has its own prescaler in control-register)</span></span>
<span class="code-line"><span class="p">}</span><span class="w"> </span><span class="k">typedef</span><span class="w"> </span><span class="n">pios_arm_timer_t</span><span class="p">;</span></span>
<span class="code-line"></span></pre></div>
<p>Der Timer ist recht einfach. Er hat ein Register, welches freilaufend ist, d.h. bei jedem Timer-Tick (beachte Prescaler!) hochgezählt wird. Außerdem besitzt der Timer über eine Art Wecker-Funktion. Ein Wert kann im Register <em>load</em> eingespeichert werden, welcher dekrementiert wird bei jedem Timer-Tick, solange bis das Register den Wert 0 erreicht. Ist 0 erreicht, wird der eingespeicherte Wert erneut eingetragen und eine Interrupt-Leitung gesetzt.</p>
<p>Die Einstellungen des Timers sind komplett über das <em>control</em>-Register möglich. Die Bedeutung der entsprechenden Bitstellen ist im BCM2835-Peripherie-Handbuch angegeben. Die Bits sind hier als C-Präprozessor-Konstanten angegeben:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="cp">#define PIOS_ARM_TIMER_32BIT 2</span></code></span>
<span class="code-line"></span>
<span class="code-line"><span class="cp">#define PIOS_ARM_TIMER_PRESCALE_1 (0 << 2)</span></span>
<span class="code-line"><span class="cp">#define PIOS_ARM_TIMER_PRESCALE_16 (1 << 2)</span></span>
<span class="code-line"><span class="cp">#define PIOS_ARM_TIMER_PRESCALE_256 (2 << 2)</span></span>
<span class="code-line"><span class="cp">#define PIOS_ARM_TIMER_PRESCALE_UNDEF (3 << 2)</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="cp">#define PIOS_ARM_TIMER_IRQ_ENABLE (1 << 5)</span></span>
<span class="code-line"><span class="cp">#define PIOS_ARM_TIMER_ENABLE (1 << 7)</span></span>
<span class="code-line"><span class="cp">#define PIOS_ARM_TIMER_HALT_IN_DEBUG (1 << 8)</span></span>
<span class="code-line"><span class="cp">#define PIOS_ARM_TIMER_FREERUNNING_ENABLE (1 << 9)</span></span>
<span class="code-line"><span class="cp">#define PIOS_ARM_TIMER_FREERUNNING_PRESCALER(a) ((a&0xff) << 16)</span></span>
<span class="code-line"></span></pre></div>
<p>Das <em>freerunning</em>-Register hat einen eigenen Prescaler, sodass das Register unabhängig vom "Wecker" betrachtet werden kann. Beachte, dass die Konstante dazu einen 8-Bit-Wert annimmt und um 16 Bits nach links schiebt - eine bessere Möglichkeit wäre schön und kommt vielleicht irgendwann(tm).</p>
<h2 id="funktionsweise-eines-timers">Funktionsweise eines Timers</h2>
<p>Ein Timer funktioniert prinzipiell so, dass im Chip ein Oszillator verbaut ist, bspw. ein Quarz-Oszillator. Dieser Quarzkristall wird durch Spannung angeregt sich zu verformen (siehe Piezo-elektrischer Effekt). Diese Verformung führt dazu, dass sich eine Ladung des Quarzes ergibt. Der Kristall schwingt gewissermaßen in einer mehr oder weniger genauen Frequenz und erzeugt somit ein periodisches Signal, im Idealfall bereits ein Rechteckssignal, aber zur Sicherheit kann das Signal durch einen Schmidt-Trigger zum Rechteck gemacht werden. Außerdem kann der Schmidt-Trigger die Spannungspegel so regeln, dass die Spannungen durch die digitale Hardware erkannt werden.</p>
<p>Der eigentliche Timer oder Zähler besteht aus einer Reihe von Flip-Flops (also 1 Bit-Speicherbausteinen). Vorstellbar sind bspw. T-Flip-Flops (Trigger-Flip-Flops), die bei einer Eingabe des logischen Wertes 1 ihren Wert wechseln. Die Speicherbausteine sind so verschaltet, dass beim Schalten des ersten (Least Significant Bit) von 1 auf 0, der nächst höhere umschaltet, etc. bspw. indem die Ausgabe des LSB-Flip-Flops als Clock-Leitung genutzt wird. Flip-Flops reagieren i.d.R. auf eine Flanke, nicht auf einen (HIGH/LOW)-Pegel.</p>
<h3 id="der-prescaler">Der Prescaler</h3>
<p>Ein Prescaler, oder auch Vorteiler, teilt die schnelleren Spannungswechsel, die vom Schmidt-Trigger erzeugt werden, mehrfach vor, sodass eine langsamere Frequenz entsteht. Für Teilungsfaktoren um den Faktor zwei werden wiederum Flip-Flops benutzt, die vor den eigentlichen Zähler geschaltet werden. Die Ausgabe der Vorteilerschaltung wird an die Eingabe des Zählers geschaltet. Die Vorteiler-Flip-Flops strecken somit die LOW bzw. HIGH-Phasen des Eingangssignals für den Zähler.</p>
<h2 id="interrupts_1">Interrupts</h2>
<p>Der ARM-Prozessor des Raspberry Pi kann genau acht Interrupt-Quellen unterscheiden. Dabei sind nicht alle Quellen tatsächlich externe Hardware, sondern Interrupts können auch vom Prozessor selbst erzeugt werden. Damit der Prozessor weiß, was zu tun ist, wenn ein Interrupt auftritt, gibt es im Speicher eine so genannte Interrupt Tabelle, die angibt, wo der Prozessor hinspringen muss (bzw. welche Befehle auszuführen sind), wenn ein bestimmter Interupt auftritt. Diese Tabelle liegt beim Raspberry Pi an der physischen Adresse 0x0000 0000, d.h. genau am Nullpunkt und ist 8 mal 8 Byte groß (2 * 4 Byte pro Interrupt-Quelle). Die Tabelle <strong>muss</strong> in Software gesetzt werden, sie kann soweit ich weiß nicht zur Compilezeit angelegt werden, wie bei den AVR-Prozessoren. Der folgende Code (von [5]) funktioniert dafür:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="n">start</span><span class="p">:</span></code></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">_reset_h</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">_undefined_instruction_vector_h</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">_software_interrupt_vector_h</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">_prefetch_abort_vector_h</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">_data_abort_vector_h</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">_unused_handler_h</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">_interrupt_vector_h</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">_fast_interrupt_vector_h</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="n">_reset_h</span><span class="p">:</span><span class="w"> </span><span class="o">.</span><span class="n">word</span><span class="w"> </span><span class="n">_reset_</span></span>
<span class="code-line"><span class="n">_undefined_instruction_vector_h</span><span class="p">:</span><span class="w"> </span><span class="o">.</span><span class="n">word</span><span class="w"> </span><span class="n">undef_vector</span></span>
<span class="code-line"><span class="n">_software_interrupt_vector_h</span><span class="p">:</span><span class="w"> </span><span class="o">.</span><span class="n">word</span><span class="w"> </span><span class="n">swi_vector</span></span>
<span class="code-line"><span class="n">_prefetch_abort_vector_h</span><span class="p">:</span><span class="w"> </span><span class="o">.</span><span class="n">word</span><span class="w"> </span><span class="n">abort_vector</span></span>
<span class="code-line"><span class="n">_data_abort_vector_h</span><span class="p">:</span><span class="w"> </span><span class="o">.</span><span class="n">word</span><span class="w"> </span><span class="n">abort_vector</span></span>
<span class="code-line"><span class="n">_unused_handler_h</span><span class="p">:</span><span class="w"> </span><span class="o">.</span><span class="n">word</span><span class="w"> </span><span class="n">_reset_</span></span>
<span class="code-line"><span class="n">_interrupt_vector_h</span><span class="p">:</span><span class="w"> </span><span class="o">.</span><span class="n">word</span><span class="w"> </span><span class="n">irq_vector</span></span>
<span class="code-line"><span class="n">_fast_interrupt_vector_h</span><span class="p">:</span><span class="w"> </span><span class="o">.</span><span class="n">word</span><span class="w"> </span><span class="n">fiq_vector</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="n">_reset_</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="c1">#0x8000</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">r1</span><span class="p">,</span><span class="w"> </span><span class="c1">#0x0000</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldmia</span><span class="w"> </span><span class="n">r0</span><span class="o">!</span><span class="p">,{</span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="n">r3</span><span class="p">,</span><span class="w"> </span><span class="n">r4</span><span class="p">,</span><span class="w"> </span><span class="n">r5</span><span class="p">,</span><span class="w"> </span><span class="n">r6</span><span class="p">,</span><span class="w"> </span><span class="n">r7</span><span class="p">,</span><span class="w"> </span><span class="n">r8</span><span class="p">,</span><span class="w"> </span><span class="n">r9</span><span class="p">}</span><span class="w"> </span><span class="o">/**</span><span class="w"> </span><span class="nb">load</span><span class="w"> </span><span class="mi">32</span><span class="w"> </span><span class="n">Byte</span><span class="w"> </span><span class="n">worth</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">**/</span></span>
<span class="code-line"><span class="w"> </span><span class="n">stmia</span><span class="w"> </span><span class="n">r1</span><span class="o">!</span><span class="p">,{</span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="n">r3</span><span class="p">,</span><span class="w"> </span><span class="n">r4</span><span class="p">,</span><span class="w"> </span><span class="n">r5</span><span class="p">,</span><span class="w"> </span><span class="n">r6</span><span class="p">,</span><span class="w"> </span><span class="n">r7</span><span class="p">,</span><span class="w"> </span><span class="n">r8</span><span class="p">,</span><span class="w"> </span><span class="n">r9</span><span class="p">}</span><span class="w"> </span><span class="o">/**</span><span class="w"> </span><span class="n">store</span><span class="w"> </span><span class="mi">32</span><span class="w"> </span><span class="n">Byte</span><span class="w"> </span><span class="o">**/</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldmia</span><span class="w"> </span><span class="n">r0</span><span class="o">!</span><span class="p">,{</span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="n">r3</span><span class="p">,</span><span class="w"> </span><span class="n">r4</span><span class="p">,</span><span class="w"> </span><span class="n">r5</span><span class="p">,</span><span class="w"> </span><span class="n">r6</span><span class="p">,</span><span class="w"> </span><span class="n">r7</span><span class="p">,</span><span class="w"> </span><span class="n">r8</span><span class="p">,</span><span class="w"> </span><span class="n">r9</span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="n">stmia</span><span class="w"> </span><span class="n">r1</span><span class="o">!</span><span class="p">,{</span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="n">r3</span><span class="p">,</span><span class="w"> </span><span class="n">r4</span><span class="p">,</span><span class="w"> </span><span class="n">r5</span><span class="p">,</span><span class="w"> </span><span class="n">r6</span><span class="p">,</span><span class="w"> </span><span class="n">r7</span><span class="p">,</span><span class="w"> </span><span class="n">r8</span><span class="p">,</span><span class="w"> </span><span class="n">r9</span><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<p>In der ersten Näherung lege ich mir folgende Interrupt-Service-Routinen an, die eine Ausgabe über UART tätigen und dann in einer Endlosschleife hängen bleiben:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="kt">void</span><span class="w"> </span><span class="n">__attribute__</span><span class="p">((</span><span class="n">interrupt</span><span class="p">(</span><span class="s">"UNDEF"</span><span class="p">)))</span><span class="w"> </span><span class="n">undef_vector</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span></code></span>
<span class="code-line"><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">pios_uart_puts</span><span class="w"> </span><span class="p">(</span><span class="s">"UNDEF :( </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="k">while</span><span class="p">(</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">)</span></span>
<span class="code-line"><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="cm">/* Do Nothing! */</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"><span class="kt">void</span><span class="w"> </span><span class="n">__attribute__</span><span class="p">((</span><span class="n">interrupt</span><span class="p">(</span><span class="s">"IRQ"</span><span class="p">)))</span><span class="w"> </span><span class="n">irq_vector</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span></span>
<span class="code-line"><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">pios_uart_puts</span><span class="w"> </span><span class="p">(</span><span class="s">" -> ! IRQ :) </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="k">while</span><span class="p">(</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">)</span></span>
<span class="code-line"><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="cm">/* Do Nothing! */</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"><span class="kt">void</span><span class="w"> </span><span class="n">__attribute__</span><span class="p">((</span><span class="n">interrupt</span><span class="p">(</span><span class="s">"FIQ"</span><span class="p">)))</span><span class="w"> </span><span class="n">fiq_vector</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span></span>
<span class="code-line"><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">pios_uart_puts</span><span class="w"> </span><span class="p">(</span><span class="s">"FIQ :( </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="k">while</span><span class="p">(</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">)</span></span>
<span class="code-line"><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="cm">/* Do Nothing! */</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"><span class="kt">void</span><span class="w"> </span><span class="n">__attribute__</span><span class="p">((</span><span class="n">interrupt</span><span class="p">(</span><span class="s">"SWI"</span><span class="p">)))</span><span class="w"> </span><span class="n">swi_vector</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span></span>
<span class="code-line"><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">pios_uart_puts</span><span class="w"> </span><span class="p">(</span><span class="s">"SWI :( </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="k">while</span><span class="p">(</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">)</span></span>
<span class="code-line"><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="cm">/* Do Nothing! */</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"><span class="kt">void</span><span class="w"> </span><span class="n">__attribute__</span><span class="p">((</span><span class="n">interrupt</span><span class="p">(</span><span class="s">"ABORT"</span><span class="p">)))</span><span class="w"> </span><span class="n">abort_vector</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span></span>
<span class="code-line"><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">pios_uart_puts</span><span class="w"> </span><span class="p">(</span><span class="s">"ABORT :(</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="k">while</span><span class="p">(</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">)</span></span>
<span class="code-line"><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="cm">/* Do Nothing! */</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<h3 id="interrupts-anschalten">Interrupts anschalten</h3>
<p>Nun weiß also mein Prozessor was zu tun ist, wenn ein Interrupt kommt. Aber er wird noch nichts machen, weil die Interrupts im Prozessor noch angeschalten werden müssen. D.h. wir können unseren Prozessor anweisen externe Interrupt (wie auch Fast Interrupts) zu ignorieren. Das ist der Standardzustand, wenn der Raspberry Pi angeschalten wird. [7] gibt an, dass das IRQ-disable-Bit mit 0x80 angesprochen werden kann. Also aktivieren wir die Interruptbehandlung, indem wir das Bit auf 0 setzen (clear):</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="n">pios_irq_enable</span><span class="o">:</span></code></span>
<span class="code-line"><span class="w"> </span><span class="n">mrs</span><span class="w"> </span><span class="n">r0</span><span class="o">,</span><span class="w"> </span><span class="n">cpsr</span></span>
<span class="code-line"><span class="w"> </span><span class="n">bic</span><span class="w"> </span><span class="n">r0</span><span class="o">,</span><span class="w"> </span><span class="n">r0</span><span class="o">,</span><span class="w"> </span><span class="err">#</span><span class="mh">0x80</span></span>
<span class="code-line"><span class="w"> </span><span class="n">msr</span><span class="w"> </span><span class="n">cpsr_c</span><span class="o">,</span><span class="w"> </span><span class="n">r0</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">pc</span><span class="o">,</span><span class="w"> </span><span class="n">lr</span></span>
<span class="code-line"></span></pre></div>
<p>Beachte, dass das Programm status register (<em>cpsr</em>) nur durch bestimmte Befehle bearbeitet werden kann (<em>mrs</em>, <em>msr</em>). Diese Befehle sind nur in bestimmten Rechteeinstellungen des Prozessors erlaubt und führen bspw. im User-Modus des Prozessors zu einem Interrupts. Das soll sicherstellen, dass Nutzerprogramme nicht in das Statusregister schreiben können und den Modus wechseln können.</p>
<h3 id="interrupt-controller">Interrupt-Controller</h3>
<p>Der ARM1176jzf-s, der im Raspberry Pi verbaut ist, hat aber noch einen weiteren Trick im Ärmel. Richtig ist, dass der ARM-Kern nur 8 Interrupt-Quellen behandeln kann. Dennoch kann ein Interrupt durch mehrere (externe) Geräte erzeugt werden oder verschiedene Gründe haben. Um präziser Interrupts ein- bzw. ausschalten zu können, hat der Pi einen Interrupt-Controller, der zusammen mit dem ARM-Kern arbeiten soll.</p>
<p>Laut Handbuch [4, S. 109 ff] ist der Controller unter der Adresse 0x2000 B200 zu erreichen (Beachte: die angegebene Adresse im Handbuch ist die, die über den ARM-Bus geht, nicht aber die Adresse, die vom ARM-Kern aus sichtbar ist). Hier sind bestimmte Register dafür zuständig Interruptquellen ein- bzw. auszuschalten. Wir wollen hier den Timer aktivieren, also schreiben wir den passenden Wert in das Base Interrupt enable register:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="c1">//*(0x2000B218) = (1<<0)</span></code></span>
<span class="code-line"><span class="n">RPI_GetIrqController</span><span class="p">()</span><span class="o">-></span><span class="n">Enable_Basic_IRQs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">RPI_BASIC_ARM_TIMER_IRQ</span><span class="p">;</span></span>
<span class="code-line"></span></pre></div>
<h2 id="den-wecker-stellen_1">Den Wecker stellen</h2>
<p>Der Timer sollte zuvor gestellt werden. Dazu schreiben wir einen gewünschten Startwert in das <em>load</em>-Register und aktivieren den Timer mit den für uns passenden Werten (32-Bit Modus, mit aktivierten Interrupts und angeschaltetem Timer):</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="c1">// pios_arm_timer->load = load;</span></code></span>
<span class="code-line"><span class="n">pios_arm_timer_setLoad</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="mh">0x2000</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="cm">/**</span></span>
<span class="code-line"><span class="cm"> * if ( !(prescaler == PIOS_ARM_TIMER_PRESCALE_1 || prescaler == PIOS_ARM_TIMER_PRESCALE_16 </span></span>
<span class="code-line"><span class="cm"> * || prescaler == PIOS_ARM_TIMER_PRESCALE_256 || prescaler == PIOS_ARM_TIMER_PRESCALE_UNDEF) )</span></span>
<span class="code-line"><span class="cm"> * {</span></span>
<span class="code-line"><span class="cm"> * prescaler = PIOS_ARM_TIMER_PRESCALE_256;</span></span>
<span class="code-line"><span class="cm"> * }</span></span>
<span class="code-line"><span class="cm"> * uint32_t val = PIOS_ARM_TIMER_32BIT | prescaler | (irq ? PIOS_ARM_TIMER_IRQ_ENABLE : 0) | PIOS_ARM_TIMER_FREERUNNING_ENABLE | (enable ? PIOS_ARM_TIMER_ENABLE:0);</span></span>
<span class="code-line"><span class="cm"> * pios_arm_timer->control = val;</span></span>
<span class="code-line"><span class="cm">**/</span></span>
<span class="code-line"><span class="n">pios_arm_timer_init</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="nb">true</span><span class="p">,</span><span class="w"> </span><span class="n">PIOS_ARM_TIMER_PRESCALE_256</span><span class="p">,</span><span class="w"> </span><span class="nb">true</span><span class="w"> </span><span class="p">);</span></span>
<span class="code-line"></span></pre></div>
<p>Nun erhalten wir Interrupts vom ARM-Timer.</p>
<p>[1] <a href="https://github.com/naums/PiOS">https://github.com/naums/PiOS<br/>
</a>[2] <a href="https://www.stefannaumann.de/de/2016/05/pilotos-prozesse/">https://www.stefannaumann.de/de/2016/05/pilotos-prozesse/</a><br/>
[3] <a href="https://www.stefannaumann.de/de/2016/05/pilotos-prozesse-2-zustaende-und-blockierung/">https://www.stefannaumann.de/de/2016/05/pilotos-prozesse-2-zustaende-und-blockierung/</a><br/>
[4] <a href="https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/BCM2835-ARM-Peripherals.pdf">https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/BCM2835-ARM-Peripherals.pdf</a><br/>
[5] <a href="http://www.valvers.com/open-software/raspberry-pi/step04-bare-metal-programming-in-c-pt4/">http://www.valvers.com/open-software/raspberry-pi/step04-bare-metal-programming-in-c-pt4/</a><br/>
[6] <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0333h/ch02s09s01.html">http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0333h/ch02s09s01.html</a><br/>
[7] <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0333h/I2837.html">http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0333h/I2837.html</a></p>Porting Micropython to Raspberry Pi2017-05-18T23:07:00+02:002017-05-18T23:07:00+02:00snaumstag:www.snaums.de,2017-05-18:/informatik/porting-micropython-to-raspberry-pi.html<p><em>This article is in English for I hope it will be useful for someone who tries to port Micropython to new hardware as I do here.</em></p>
<p>Micropython [1] is a very small Python interpreter, which can run on very restricted hardware. The pyboard is based on an STM32-F04 ARM processor …</p><p><em>This article is in English for I hope it will be useful for someone who tries to port Micropython to new hardware as I do here.</em></p>
<p>Micropython [1] is a very small Python interpreter, which can run on very restricted hardware. The pyboard is based on an STM32-F04 ARM processor, with 1 KiB of Flash-ROM and 192 KiB of RAM. Micropython can and was ported to different hardware before. But the Raspberry Pi seems a bit overpowered im comparison to the F04-processor.</p>
<p>This text describes the way to built a <strong>bare-metal </strong>port of Micropython for the Raspberry Pi. Using the article you will build a Micropython running on the Raspberry Pi as Operating System, there is no Unix running to aid Micropython with anything.</p>
<h2 id="software-environment"><strong>Software Environment</strong></h2>
<p>I have to computer to build Micropython on: An Arch Linux and a (very old) Linux Mint 17.1. You will need the following software:</p>
<ul>
<li>arm-none-eabi-gcc</li>
<li>arm-none-eabi-binutils</li>
</ul>
<p>Micropython does some calls to a C-library, which I use <strong>newlib </strong>[2] for. My code can be found under [3]. The Makefile contains rules for building newlib seperately by invoking <code>make newlib</code>. This will call the configure-script of newlib and compile it to a static libary especially for the Raspberry Pi.</p>
<p>You will probably want to use a tool like Raspbootin [4] to transfer the binary to the Raspberry Pi. Raspbootin makes it possible to transmit compiled code to the Pi via a serial (i.e. UART) interface to the Pi.</p>
<h3 id="details-on-the-newlib-build">Details on the newlib-build</h3>
<p>Building newlib for the Raspberry Pi was quite a challenge for me, as I struggled at first to build it with support for the Raspberry Pis Vector Floating Point Unit (VFPv2). The following setup seems to work fine:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code>CFLAGS_FOR_TARGET="-mcpu=arm1176jzf-s -mfpu=vfp -marm -mfloat-abi=hard"</code></span>
<span class="code-line">configure --target=arm-none-eabi --enable-newlib-hw-fp --with-float=hard --with-cpu=arm1176jzf-s \</span>
<span class="code-line"> --with-fpu=vfp --disable-multilib --disable-shared --enable-target-optspace \</span>
<span class="code-line"> --disable-newlib-supplied-syscalls</span>
<span class="code-line"></span></pre></div>
<p>At first I need to specify the CPU of the build, i.e. the CPU I want to build for. The Raspberry Pi BCM2835-chip contains a arm1176jzf-s processor. Be aware, that this only holds true for the Raspberry Pi 1 Model A, Model B+, etc. but not for the Raspberry Pi 2 or 3. Then I instruct the configure-script still inside the <em>CFLAGS_FOR_TARGET</em>-field, to build float-abi hard and vfp-support, and to emit ARM-code not code for the Thumb-mode of that processor.</p>
<p>The configure call itself instructs configure to use the target arm-none-eabi, enables the hardware floating point unit, disables multilib, i.e. disables that a Thumb-version is build, disables that a shared libary is build and disables syscalls inside newlib. The latter is important because I do (did) not want to write Interrupt Service Routines for the Raspberry Pi (yet).</p>
<h3 id="syscalls">Syscalls</h3>
<p>Newlib is a C-library. A C-library contains call which need syscalls for working correctly, for example <em>printf</em> needs a <em>write</em>-syscall so it can output anything to <em>stdout</em>. It is our job to implement <em>write</em>, so newlib can use the "syscall" and <em>printf</em> can output anything. David Welch also has code prepared for that job.</p>
<h2 id="porting-micropython_1">Porting Micropython</h2>
<p>Porting Micropython to the Raspberry Pi was esentially quite easy - at least to get [any]{style="text-decoration: underline;"} response from the Micropython. Esentially I took a lot of working code from David Welch, his AUX_UART-code to be exact and used that for the serial connection between the Raspberry Pi running Micropython and my PC. I was working on a clean rewrite of that code, but that did not work out that well yet, so I continue using this code for the time being.</p>
<p>I starting with the folder <em>bare-arm</em> and started tweaking its Makefile until it built binaries for the Raspberry Pi. The bare-arm-port is written for an Cortex-M4 processor, which in turn I needed to replace with the ARM1176jzf-s processor of the Raspberry Pi.</p>
<h3 id="port-structure">Port structure</h3>
<p>The folder contains the following files, which are or may be important to your port:</p>
<ul>
<li><em>Makefile</em> - duh!</li>
<li><em>mphalport.h</em> - I did not really use that file, so I don't know what it was for</li>
<li><em>mphalconfigport.h</em> - defines loads of options how to build Micropython. You can change a lot of builtin-stuff here</li>
<li><em>qstrdefsport.h</em> - defines QStrings, which are used by your port specifically</li>
<li><em>kernel.ld</em> - The Linker skript</li>
</ul>
<h4 id="on-qstrings">On QStrings</h4>
<p>The QStrings are strings, which are held only once in Memory even if they are used by two completely separate parts of the code. The reason why you have to write them there in the following way, is that the build-process creates constants from them and builds them into Micropython only once. It needs to check whether any string is used twice and build it only once.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="n">Q</span><span class="p">(</span><span class="n">hello</span><span class="p">)</span></code></span>
<span class="code-line"><span class="n">Q</span><span class="p">(</span><span class="n">inc</span><span class="p">)</span></span>
<span class="code-line"><span class="n">Q</span><span class="p">(</span><span class="n">dec</span><span class="p">)</span></span>
<span class="code-line"><span class="n">Q</span><span class="p">(</span><span class="n">offset</span><span class="p">)</span></span>
<span class="code-line"><span class="c1">// ...</span></span>
<span class="code-line"></span></pre></div>
<p>More on these QStrings in a future article.</p>
<h4 id="configuring-the-builtins-of-the-port">Configuring the builtins of the port</h4>
<p>Using preprocessor-statements in the file <em>mphalconfigport.h </em>you can change what parts of Micropython are built into the binary. This may be essential to ports to hardware with very limited resources, as every module, which is built into CPython by default can increase the binary size and in turn the memory footprint of the interpreter at run-time.</p>
<p>The options are endless. For a (I believe not complete) list of options and a very short description on what they do you can consult the file <em>/py/mpconfig.h</em> in the project repository [5].</p>
<p>There are very important other options there. For example you have to define what modules are built into Micropython, maybe you want to write module for it yourself to use in your Python code. But more on this in a later article. Also there is the macro <em>[MP_PLAT_PRINT_STRN]{.pl-en}([str, len]{.pl-v}) </em>which you have to define as a print-function. I used it as macro for a print-string-function, which prints a string through the UART-interface of the Raspberry Pi.</p>
<h4 id="the-linker-script">The linker script</h4>
<p>The linker script tells our linker where a particular part of our code should be placed in the binary. I used the <em>kernel.ld</em> linker script of the Baking Pi-Tutorial [6]. This is a quite simple script but defines all the needed areas which are used by our compiler and states where they should be placed.</p>
<h3 id="the-more-complete-picture_1">The more complete picture</h3>
<p>After adding some important information, like that I want to link the binary against newlibs <em>libc</em> and <em>libm</em>-libraries and the <em>libgcc</em> I need to add the code-files that should be built into Micropython. This can be done by adding them to the <em>SRC_C</em> and the <em>SRC_S</em>-Variables (for Assembler-files).</p>
<p>At first I had only one Assembler-file (<em>startup_rpi.s</em>) which directly jumped into the main-routine which is defined in a C-file:</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="na">.section</span><span class="w"> </span><span class="no">.init</span></code></span>
<span class="code-line"><span class="na">.globl</span><span class="w"> </span><span class="no">_start</span></span>
<span class="code-line"><span class="na">.globl</span><span class="w"> </span><span class="no">blinkloop</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="nl">_start:</span></span>
<span class="code-line"><span class="w"> </span><span class="nf">mov</span><span class="w"> </span><span class="no">sp</span><span class="p">,</span><span class="w"> </span><span class="mi">#0</span><span class="no">x8000</span></span>
<span class="code-line"><span class="w"> </span><span class="nf">bl</span><span class="w"> </span><span class="no">main</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="nl">blinkloop:</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// ...</span></span>
<span class="code-line"><span class="w"> </span><span class="nf">b</span><span class="w"> </span><span class="no">blinkloop</span></span>
<span class="code-line"></span></pre></div>
<p>The main-routine then sets up the Micrpython and runs little test-skripts. It also needs to set the stack accordingly, so that Micropython will not run out of memory instandly. On the first run I expect the code to just run and work perfectly, but instead the statements fail, which I find very interesting. The follwing code was basically copied from the <em>bare-arm</em>-folder.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="kt">void</span><span class="w"> </span><span class="nf">do_str</span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">mp_parse_input_kind_t</span><span class="w"> </span><span class="n">input_kind</span><span class="p">)</span><span class="w"> </span><span class="p">{</span></code></span>
<span class="code-line"><span class="w"> </span><span class="n">mp_lexer_t</span><span class="w"> </span><span class="o">*</span><span class="n">lex</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mp_lexer_new_from_str_len</span><span class="p">(</span><span class="n">MP_QSTR__lt_stdin_gt_</span><span class="p">,</span><span class="w"> </span><span class="n">src</span><span class="p">,</span><span class="w"> </span><span class="n">strlen</span><span class="p">(</span><span class="n">src</span><span class="p">),</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">lex</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="k">return</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">nlr_buf_t</span><span class="w"> </span><span class="n">nlr</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">nlr_push</span><span class="p">(</span><span class="o">&</span><span class="n">nlr</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">qstr</span><span class="w"> </span><span class="n">source_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">lex</span><span class="o">-></span><span class="n">source_name</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mp_parse_tree_t</span><span class="w"> </span><span class="n">parse_tree</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mp_parse</span><span class="p">(</span><span class="n">lex</span><span class="p">,</span><span class="w"> </span><span class="n">input_kind</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mp_obj_t</span><span class="w"> </span><span class="n">module_fun</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mp_compile</span><span class="p">(</span><span class="o">&</span><span class="n">parse_tree</span><span class="p">,</span><span class="w"> </span><span class="n">source_name</span><span class="p">,</span><span class="w"> </span><span class="n">MP_EMIT_OPT_NONE</span><span class="p">,</span><span class="w"> </span><span class="nb">true</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mp_call_function_0</span><span class="p">(</span><span class="n">module_fun</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="n">nlr_pop</span><span class="p">();</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// uncaught exception</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mp_obj_print_exception</span><span class="p">(</span><span class="o">&</span><span class="n">mp_plat_print</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">mp_obj_t</span><span class="p">)</span><span class="n">nlr</span><span class="p">.</span><span class="n">ret_val</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="p">}</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">argc</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">**</span><span class="n">argv</span><span class="p">)</span><span class="w"> </span><span class="p">{</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mp_init</span><span class="p">();</span></span>
<span class="code-line"><span class="w"> </span><span class="n">do_str</span><span class="p">(</span><span class="s">"print('hellp world')"</span><span class="p">,</span><span class="w"> </span><span class="n">MP_PARSE_SINGLE_INPUT</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="n">do_str</span><span class="p">(</span><span class="s">"print('hello world!', list(x+1 for x in range(10)), end='eol</span><span class="se">\\</span><span class="s">n')"</span><span class="p">,</span><span class="w"> </span><span class="n">MP_PARSE_SINGLE_INPUT</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="n">do_str</span><span class="p">(</span><span class="s">"for i in range(10):</span><span class="se">\n</span><span class="s"> print(i)"</span><span class="p">,</span><span class="w"> </span><span class="n">MP_PARSE_FILE_INPUT</span><span class="p">);</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mp_deinit</span><span class="p">();</span></span>
<span class="code-line"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<p>* do_str<em> takes two parameters. The second parameter is in accordance to the Python-function </em>compile*. Using single-input force Micropython to interpret the statement as single statement and therefore will only interpret a single statement at a time, whereas file-input allows for bigger scripts.</p>
<h2 id="the-bigger-picture_1">The bigger picture</h2>
<p>Now we have a running Micropython interpreter on Raspberry Pi running without an Operating System. Of course this Micropython can not be used for anything at the moment, there are basically no drivers, there is no video output, no GPIO-controlling and no input or output. So there is still a lot of work to do for the Raspberry Pi port to become finished or usable. But this is a start, even if a very small one.</p>
<p>But I do not only aim at porting Micropython but also at documenting it along the way as far as I'm comfortable. The code of Micropython is largely without any useful comments, functions are used, and a new developer has no chance but to guess what a function name may mean or what this function could possibly do.</p>
<h3 id="todo">ToDo</h3>
<ul>
<li>Build some sort of REPL or interactive mode, so automatic testing becomes trivial</li>
<li>Run longer Python code and find out why it fails and/or crashes</li>
</ul>
<p>[1] <a href="https://www.micropython.org">micropython.org</a><br/>
[2] <a href="https://sourceware.org/newlib/">sourceware.org/newlib</a><br/>
[3] <a href="https://www.github.com/naums/micropython">github.com/naums/micropython</a><br/>
[4] <a href="https://github.com/naums/raspbootin">github.com/naums/raspbootin<br/>
</a>[5] <a href="https://github.com/micropython/micropython/blob/master/py/mpconfig.h">github.com/micropython/micropython/blob/master/py/mpconfig.h<br/>
</a>[6] <a href="http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/">cl.cam.ac.uk/projects/raspberrypi/tutorials/os/</a></p>PilotOS: Prozesse 2 (Zustände und Blockierung)2016-05-22T09:30:00+02:002016-05-22T09:30:00+02:00snaumstag:www.snaums.de,2016-05-22:/informatik/pilotos-processes-2-de.html<p>In meinem ersten Artikel über Threads stand die konkrete Implementierung im Vordergrund, für diesen Artikel gibt es (noch) keine Implementierung in PilotOS, daher hier etwas Theorie über Prozesse, deren Zustände und Blockierung.</p>
<p>Ein Prozess ist eine Einheit der Ausführung oder Code, der sich in Ausführung befindet. Bei der Definition spielt …</p><p>In meinem ersten Artikel über Threads stand die konkrete Implementierung im Vordergrund, für diesen Artikel gibt es (noch) keine Implementierung in PilotOS, daher hier etwas Theorie über Prozesse, deren Zustände und Blockierung.</p>
<p>Ein Prozess ist eine Einheit der Ausführung oder Code, der sich in Ausführung befindet. Bei der Definition spielt es keine Rolle, ob der Code tatsächlich gerade ausgeführt wird, sondern es genügt, wenn der Code "bereit" ist zur Ausführung, d.h. der Code ist anspringbar, hat Speicher, und könnte theoretisch ausgeführt werden. In realen Betriebssystemen gibt es aber einige Constraints, wann ein Prozess ausgeführt wird. Dazu wurden Prozesszustände eingeführt.</p>
<p>Ein Prozesszustand gibt an, in welchem Zustand eines Automaten sich ein Prozess befindet. Ein Prozess kann meist nur dann ausgeführt werden, wenn er sich im Speicher befindet (siehe virtueller Speicher, MMU, Paging), und nicht auf andere Prozesse wartet. Ein Prozess kann beispielsweise dann auf andere Prozesse wechseln, wenn sich der andere Prozess ein Betriebsmittel genommen hat, welches nur exklusiv genutzt werden kann, aber beide Prozesse benötigen.</p>
<p>Betriebsmittel werden vom Betriebssystem verwaltet. Man unterscheide physische und logische BMs. Physisch ist bspw. die Festplatte, der Speicher; logisch eine Datei. Um einen sicheren Zugriff auf Betriebsmittel gibt es in jedem Betriebssystem den gegenseitigen Ausschluss (Mutex, mutual exclusion) um sicherzustellen, dass nur ein Prozess (bzw. genereller nur k Prozesse) einen kritischen Abschnitt ausführen dürfen. Ein kritischer Abschnitt ist Code, der, wenn er von mehreren Prozessen nebenläufig ausgeführt wird, zu nicht mehr gutzumachendem Schaden im System führen kann.</p>
<p>Es gibt einige Designkritierien für die Implementierung von Mutex. Bspw. Lebendigkeit, d.h. kein Prozess hält ein Betriebsmittel unendlich lange, Sicherheit, d.h. etwas nicht mehr gut zu machendes schlechtes darf nicht passieren, Fairness, die unterschiedlich definiert sein kann. Hier nehmen wir folgende Fairness an: kein Prozess muss unendlich lang warten. Anhand dieser Kritieren kann eine Policy implementiert werden, die angibt wie und wann wartende Prozesse freigegeben werden.</p>
<p>Ein Blockieren eines Prozesses kann entweder vom Prozess selbst erfolgen, oder durch das Betriebssystem beim Zugriff auf ein Betriebsmittel. Bspw. ist es manchmal notwendig auf andere Prozesse zu warten, um synchronisiert arbeiten zu können. Die erste Möglichkeit um auf andere Prozesse zu warten ist einen Busy Wait zu implementieren; d.h. eine while-Schleife, die nachschaut, ob die Bedingung, auf die gewartet wird, schon erfüllt ist. Das ist aber sehr verschwenderisch auf Rechenzeit, weil der Scheduler diesen Prozess weiterhin plant und dieser Prozess sein Zeitintervall nur dafür verwendet zu warten.</p>
<p>Sinnvoller wäre es, diesen Prozess nicht mehr zu schedulen bis die Bedingung erfüllt ist. Dazu setzt man seinen Prozesszustand auf "wartend" oder "blockiert", was angibt, dass der Prozess auf etwas wartet. Die Bedingung muss ebenfalls gespeichert werden (bspw. in Form eines Mutex-Deskriptors). Wird dieses Mutex (bspw. in Form eines Semaphors implementiert) freigegeben, setzt das Betriebssystem alle darauf wartenden Prozesse in den "bereit"-Zustand. Je nachdem, wie viele Prozesse in den kritischen Abschnitt dürfen, kann auch nur ein Prozess ausgewählt werden, der in den "bereit"-Zustand wechselt.</p>
<p>D.h. wir haben grundlegend drei Prozesszustände: "laufend", d.h. der Prozess wird in diesem Moment von der CPU ausgeführt, "aktiv" - der Prozess wartet auf die CPU, kann aber ausgeführt werden und "blockiert, wartend" - der Prozess wartet auf Freigabe eines Mutex.</p>
<p>Ein Mutex kann verschiedenen implementiert sein. Es gibt in gewöhnlichen Betriebssystemen meist eine Semaphor-Implementierung, aber auch Monitore. Alternativ kann das auch durch eine bool'sche Zustandsvariable realisiert werden. Der Trick dabei ist, dass, wenn mehrere Prozesse involviert sind, die Implementierung etwas schwierig ist. So muss beim Eintritt in einen kritischen Abschnitt geprüft werden, ob die Variable gesetzt ist (bzw. die Semaphore einen bestimmten Wert erreicht hat). Falls ja, darf der Prozess nicht in den kritischen Abschnitt und muss warten.</p>
<p>Ist die Variable nicht gesetzt, darf der Prozess in den kritischen Abschnitt, muss aber noch (mit einem zweiten Befehl) die Variable setzen. Wird er jetzt verdrängt, kann problemlos ein zweiter Prozess in den kritischen Abschnitt und etwas Schlimmes könnte passieren (siehe Race Condition). Dafür haben die meisten Prozessoren einen Befehl, der gleichzeitig eine Variable testen kann, und sie setzt, sodass kein anderer Prozess in den kritischen Abschnitt gelangen kann.</p>
<p>Alternativ könnte für den Zeitraum, in dem sich ein Prozess im kritischen Abschnitt befindet, der Interrupt ausgeschaltet werden. Das hätte zur Folge, dass der Prozess nicht durch Timer-Interrupts unterbrochen werden kann, aber auch, dass er generell nicht unterbrochen werden kann. Wird diese Methode also angewandt, muss der Programmierer höllisch darauf achten, dass sein kritischer Abschnitt sehr kurz ist, um das System nicht unnötig auszubremsen, da in dieser Zeit kein anderer Prozess ausgeführt werden kann. Wiederum könnt er die Interrupts auch nur für den Zeitraum deaktivieren, in dem er die Variable testet und setzt, und dann wieder aktivieren.</p>PilotOS: Prozesse2016-05-21T12:14:00+02:002016-05-21T12:14:00+02:00snaumstag:www.snaums.de,2016-05-21:/informatik/pilotos-processes-de.html<p>PilotOS [1] soll ein einfaches Betriebssystem für den Raspberry Pi werden. Der Sinn hinter dem Betriebssystem ist hauptsächlich der Wissenszuwachs für mich, und die Einblicke in die Entwicklung von Betriebssystem, angefangen von einfachen Funktionalitäten, bis hin zur Architektur von Betriebssystemen. PilotOS zielt dabei in keinster Weise auf Benutzbarkeit, sondern dient …</p><p>PilotOS [1] soll ein einfaches Betriebssystem für den Raspberry Pi werden. Der Sinn hinter dem Betriebssystem ist hauptsächlich der Wissenszuwachs für mich, und die Einblicke in die Entwicklung von Betriebssystem, angefangen von einfachen Funktionalitäten, bis hin zur Architektur von Betriebssystemen. PilotOS zielt dabei in keinster Weise auf Benutzbarkeit, sondern dient einzig und allein den Spielerei-Zwecken.</p>
<p>Gestern habe ich Prozesse hinzugefügt und einen einfachen Scheduler sowie Dispatcher für Prozesse. Ein Dispatcher ist dafür da, Prozesse umzuschalten, d.h. die CPU anzuweisen in einen anderen Prozess zu springen, sowie dessen Kontext wiederherzustellen, nachdem der Kontext des verdrängten Prozess abgespeichert worden ist. Scheduling stellt die Strategie dar, nach welcher der nächste Prozess gewählt wird.</p>
<p>Es gibt sowohl kooperatives als auch preemptives Umschalten. Beim kooperativen Umschalten geben die Prozesse ihre Kontrolle über die CPU freiwillig ab, bspw. durch Rufen einer bestimmten Funktion. Manchmal ist das auch so implementiert, dass jeder Systemruf dazu führt, dass umgeschalten werden kann. Preemptives Umschalten wird durch Timer-Interrupts realisiert. D.h. beim Start des Systems wird der Hardware-Timer auf eine bestimmte Zeit programmiert, nach der er einen Interrupt auslösen soll. Dieser Interrupt wird von der CPU gefangen, und in einer bestimmten Interrupt Service Routine (ISR) verarbeitet. Diese ISR sorgt dann für das Umschalten. Das hat zur Folge, dass jeder Prozess zu jedem Zeitpunkt umgeschalten werden kann (siehe Race Conditions).</p>
<p>In PilotOS ist aktuell ein kooperatives Umschalten implementiert. Genau genommen steht erst einmal nur die Möglichkeit für kooperatives Umschalten. Der Code wird aktuell noch nirgends verwendet, und ist noch ungetestet. Der Scheduler wählt einen Prozess aus, mithilfe einer Vergleichfunktion, die anhand irgendeines beliebigen Merkmals aus zwei Prozessen (bzw. Prozess-Kontroll-Blöcken, PCB) einen zurück liefert. Die eigentliche Scheduling-Routine wendet diese Funktion also auf alle PCBs an. Ein PCB beinhaltet Informationen über die Prozesse, hier sind das Informationen wie ID, Deadline, Execution-Time, Last-Use-Informationen und der StackPointer. Nicht alle Attribute werden aktuell (sinnvoll) eingesetzt.</p>
<p>Zum Abgeben der Kontrolle der CPU gibt es die Funktion <em>yield</em>, die direkt in die Funktion <em>dispatch</em> springt. Die Trennung der beiden Funktionen ist aktuell unnötig, kann aber eventuell später nützlich sein, daher habe ich das erst einmal so implementiert. Der Dispatcher speichert sich das Link-Register (lr) auf dem Stack. Das Link-Register gibt bei ARM die Rücksprungaddresse an, nach einem Link-Branch (Assembler-Befehl <em>bl</em>), der eine Art Funktionsaufruf darstellt. In der aufgerufenen Funktion steht dann das Link-Register auf dem nächsten Befehl, in dem Code vor dem Funktionsruf, d.h. mithilfe von <em>mov pc, lr</em> lässt sich wieder zurückspringen (PC - Program Counter, Instruction Pointer).</p>
<p>Dann werden alle Register (r1,..r12) gespeichert. r13, r14, r15 werden nicht mit abgespeichert, weil diese besondere Funktionen im ARM-Kern haben. r13 ist der Stackpointer - den auf den Stack weg zu speichern wäre relativ sinnlos. r14 ist das Link-Register. Man könnt überlegen, ob es Sinn machen würde, dieses Register zusammen mit den anderen abzuspeichern - dazu müsst ich mir die Semantik von <em>push</em> und <em>pop</em> genauer zu Gemüte führen. Vor allem müsst geklärt werden, inwiefern die Befehle die Register gleichzeitig speichern und laden, oder ob der Befehl vom Assembler in mehrere kleinere Befehle zerteilt wird. Den Programm-Counter (r15) auf dem Stack zu speichern ist komplett sinnlos, weil der aktuell in der <em>dispatch</em>-Funktion steckt. Den abzuspeichern, und wieder zu laden würde verhindern, dass wir diese Funktion jemals wieder verlassen.</p>
<p>Nachdem die Register abgespeichert sind, können sie verwendet werden, ohne riskieren zu müssen, dass Werte des eigentlichen Prozesses verloren gehen. Zunächst müssen wir aber den Stackpointer des verdrängten Prozesses in den PCB schreiben. Nun lade ich die Adressen der Funktionen, die von der <em>schedule</em>-Funktion benutzt werden sollen, um den nächsten Prozess zu finden und rufe die Funktion. Das Ergebnis liegt in r0. Nun muss nur der Stackpointer umgesetzt und die zuvor gespeicherten Register wieder geladen werden. Zum Schluss hole ich dann das Link-Register noch vom Stack, allerdings speichere das Ergebnis im Program-Counter, sodass der nächste ausgeführte Befehl der nächste Befehl des Prozesses ist, der nun ausgeführt werden soll.</p>
<p>[1] <a href="https://github.com/naums/pilotOS">https://github.com/naums/pilotOS</a></p>
<p>Beitragsbild: <a href="https://openclipart.org/detail/175363/pilot-penguin">https://openclipart.org/detail/175363/pilot-penguin</a></p>ChibiOS/RT für Raspberry Pi2016-03-17T15:00:00+01:002016-03-17T15:00:00+01:00snaumstag:www.snaums.de,2016-03-17:/informatik/chibiosrt-fuer-raspberry-pi.html<p>Hallo.</p>
<p>ChibiOS/RT [1] ist ein sehr kleines Realtime-Operating System. Es unterstützt Plattformen, die Linux bspw. nicht mehr abdeckt und kann für allerlei Spielerei benutzt werden. ChibiOS/RT kann keine Prozesse zur Laufzeit erstellen oder nachladen, Programme für das System müssen in den Betriebssystemkern eingebaut werden. Ich habe versucht die …</p><p>Hallo.</p>
<p>ChibiOS/RT [1] ist ein sehr kleines Realtime-Operating System. Es unterstützt Plattformen, die Linux bspw. nicht mehr abdeckt und kann für allerlei Spielerei benutzt werden. ChibiOS/RT kann keine Prozesse zur Laufzeit erstellen oder nachladen, Programme für das System müssen in den Betriebssystemkern eingebaut werden. Ich habe versucht die Anleitung unter [2] nachzuvollziehen, um ChibiOS/RT für den Raspberry Pi zu bauen. Meine Plattform: Linux Mint 17.1 Rebecca. Weil ich damit hinreichend große Probleme hatte, erläutere ich hier, was aktuell ein valides Vorgehen ist um den Betriebssystemkern zu bauen.</p>
<h3 id="was-wird-benotigt">Was wird benötigt?</h3>
<p>Ihr benötigt folgende Software:</p>
<ul>
<li><em>arm-none-eabi-gcc</em> Cross-Compiler, am Besten aus [3]</li>
<li><em>git</em></li>
<li><em>screen</em></li>
</ul>
<p>Folgende Hardware wird benötigt:</p>
<ul>
<li>Raspberry Pi</li>
<li>Adafruit TTL-USB-Kabel [4]</li>
</ul>
<h3 id="vorgehen">Vorgehen</h3>
<p>Zunächst wird ein passender Compiler gebraucht. Es ist offenbar möglich das Betriebssystem auf dem Pi selbst zu bauen, ich habe das Betriebssystem auf einem x86-Rechner gebaut. Dazu wird ein Cross Compiler benötigt. Ein Cross Compiler ist ein Compiler, der auf einer Plattform läuft (hier x86), aber Programme für eine andere Plattform erzeugt (hier ARMv6). Den <em>arm-none-eabi</em> aus den Paketquellen hatte ich verwendet (das war eine 4.8.1), das hat aber nicht funktioniert. Das reparieren von internen Headerfiles hatte ich mich nicht lange angetan.</p>
<p>Daher lieber den Compiler von [3] nehmen, der wird offenbar von ARM-Mitarbeitern gepflegt, und funktioniert. Außerdem ist das eine 5.2.1. Probiert nun aus, ob der Compiler gefunden wird, bspw. so: <em>arm-none-eabi-gcc --version .</em></p>
<p>Als nächstes braucht ihr den ChibiOS/RT Quellcode. Ich habe den recht veralteten Code aus dem Artikel genommen, der ein vorbereitetes Makefile für den Pi zu bieten hat. Im offiziellen Code habe ich das nicht gefunden. Zum Laden des Quellcodes wird <em>git</em> benötigt. <code class="EnlighterJSRAW" data-enlighter-language="null">git clone https://github.com/steve-bate/ChibiOS-RPi</code></p>
<p>Nun navigiert ihr in das Verzeichnis <em>demos/ARM11-BCM2835-GCC/</em> und ruft <em>make</em>. Wenn alles gut geht, solltet ihr jetzt im <em>build</em>-Ordner eine Datei namens <em>ch.bin</em> haben. Kopiert diese Datei auf eine SD-Karte mit dem Namen <em>kernel.img</em>, wo bereits ein Raspbian installiert ist. Die alte <em>kernel.img</em> solltet ihr sicherheitshalber kopieren, wenn ihr das Linux wieder benutzen wollt.</p>
<p>Für die Kommunikation mit ChibiOS/RT wird eine serielle Verbindung benötigt, bspw. mit dem Adafruit TTL-Kabel [4]. Verbindet das Kabel wie unter [5] dargestellt. Startet in einem Terminal die serielle Konsole: <code class="EnlighterJSRAW" data-enlighter-language="null">sudo screen /dev/ttyUSB0 115200</code></p>
<p>Schaltet den Pi an, indem ihr entweder das rote Kabel an den GPIO-Pin anschließt, oder das Stromkabel des Pi an den Micro-USB-Anschluss. Ihr solltet im Terminalfenster die Kommandozeile von ChibiOS/RT vorfinden. Mit dem Befehl <em>help</em> listet euch das Betriebssystem die (wenigen) Befehle auf.</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="nv">ChibiOS</span><span class="o">/</span><span class="nv">RT</span><span class="w"> </span><span class="nv">Shell</span></code></span>
<span class="code-line"><span class="nv">ch</span><span class="o">></span><span class="w"> </span><span class="nv">help</span></span>
<span class="code-line"><span class="nv">Commands</span>:<span class="w"> </span><span class="nv">help</span><span class="w"> </span><span class="k">exit</span><span class="w"> </span><span class="nv">info</span><span class="w"> </span><span class="nv">systime</span><span class="w"> </span><span class="nv">reboot</span><span class="w"> </span></span>
<span class="code-line"><span class="nv">ch</span><span class="o">></span><span class="w"> </span><span class="nv">info</span></span>
<span class="code-line"><span class="nv">Kernel</span>:<span class="w"> </span><span class="mi">2</span>.<span class="mi">5</span>.<span class="mi">1</span><span class="nv">unstable</span></span>
<span class="code-line"><span class="nv">Compiler</span>:<span class="w"> </span><span class="nv">GCC</span><span class="w"> </span><span class="mi">5</span>.<span class="mi">2</span>.<span class="mi">1</span><span class="w"> </span><span class="mi">20151202</span><span class="w"> </span><span class="ss">(</span><span class="nv">release</span><span class="ss">)</span><span class="w"> </span>[<span class="nv">ARM</span><span class="o">/</span><span class="nv">embedded</span><span class="o">-</span><span class="mi">5</span><span class="o">-</span><span class="nv">branch</span><span class="w"> </span><span class="nv">revision</span><span class="w"> </span><span class="mi">231848</span>]</span>
<span class="code-line"><span class="nv">Architecture</span>:<span class="w"> </span><span class="nv">ARM11</span></span>
<span class="code-line"><span class="nv">Core</span><span class="w"> </span><span class="nv">Variant</span>:<span class="w"> </span><span class="nv">ARM1176JZF</span><span class="o">-</span><span class="nv">S</span></span>
<span class="code-line"><span class="nv">Port</span><span class="w"> </span><span class="nv">Info</span>:<span class="w"> </span><span class="nv">Pure</span><span class="w"> </span><span class="nv">ARM</span><span class="w"> </span><span class="nv">mode</span></span>
<span class="code-line"><span class="nv">Platform</span>:<span class="w"> </span><span class="nv">BCM2835</span></span>
<span class="code-line"><span class="nv">Build</span><span class="w"> </span><span class="nv">time</span>:<span class="w"> </span><span class="nv">Mar</span><span class="w"> </span><span class="mi">17</span><span class="w"> </span><span class="mi">2016</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">13</span>:<span class="mi">11</span>:<span class="mi">12</span></span>
<span class="code-line"><span class="nv">ch</span><span class="o">></span><span class="w"> </span><span class="nv">systime</span></span>
<span class="code-line"><span class="mi">8434</span></span>
<span class="code-line"><span class="nv">ch</span><span class="o">></span></span>
<span class="code-line"></span></pre></div>
<p>Nun könnt ihr anfangen für das Echtzeitbetriebssystem Programme zu entwickeln. Dafür müsst ihr das Betriebssystem immer wieder neu bauen, aber das ist nicht sonderlich langwierig.</p>
<p>[1] <a href="http://www.chibios.org">http://www.chibios.org</a><br/>
[2] <a href="http://www.stevebate.net/chibios-rpi/GettingStarted.html">http://www.stevebate.net/chibios-rpi/GettingStarted.html<br/>
</a>[3] <a href="https://launchpad.net/gcc-arm-embedded">https://launchpad.net/gcc-arm-embedded</a><br/>
[4] <a href="https://www.adafruit.com/products/954">https://www.adafruit.com/products/954</a><br/>
[5] <a href="https://learn.adafruit.com/adafruits-raspberry-pi-lesson-5-using-a-console-cable/connect-the-lead">https://learn.adafruit.com/adafruits-raspberry-pi-lesson-5-using-a-console-cable/connect-the-lead</a></p>Raspberry Pi Betriebssystem | Assembler2015-12-30T14:39:00+01:002015-12-30T14:39:00+01:00snaumstag:www.snaums.de,2015-12-30:/informatik/raspberry-pi-betriebssystem-assembler.html<p>Hallo.</p>
<p>Aktuell beschäftige ich mich mit dem Raspberry Pi und dem enthaltenen BCM2835-Chip, sowie mit ARM-Assembler. Dazu verfolge ich dieses Tutorial der Cambridge University <a href="http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/">[1]</a>. Das Tutorial führt in das Thema Assembler ein, und zeigt einige interessante Wege den Pi einfache Sachen tun zu lassen. Angefangen davon eine GPIO-LED blinken …</p><p>Hallo.</p>
<p>Aktuell beschäftige ich mich mit dem Raspberry Pi und dem enthaltenen BCM2835-Chip, sowie mit ARM-Assembler. Dazu verfolge ich dieses Tutorial der Cambridge University <a href="http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/">[1]</a>. Das Tutorial führt in das Thema Assembler ein, und zeigt einige interessante Wege den Pi einfache Sachen tun zu lassen. Angefangen davon eine GPIO-LED blinken zu lassen, bis hin zur Benutzung von Maus und Tastatur sowie Bildschirm. Das Tutorial ist gut und anfängerfreundlich geschrieben und lässt euch auch die Möglichkeit selbst zu probieren und selbst Code zu schreiben, und damit zu testen ob ihr die Theorie verstanden habt.</p>
<p>Der gebaute Kern (siehe dazu unter Downloads das Makefile) wird auf eine SD-Karte vom Raspberry Pi kopiert, wo sich bereits ein Raspbian Kernel befunden hat. Dort wird kernel.img überschrieben. Das führt dazu, dass der enthaltene Bootloader euren Kernel anstelle den Linux-Kern bootet.</p>
<p>Der Assembler-Code von ARM ist recht simpel (sind ja auch RISC-CPUs). Ein sehr gutes Nachschlagewerk dafür ist <a href="http://www.heyrick.co.uk/armwiki/Main_Page">[2]</a>.</p>
<p>Ich kann jedem Informatiker empfehlen sich mit der Hardware und der Programmierung eines Raspberry Pis auf der Ebene auseinander zu setzen. Die Hardware ist hinreichend gut beschrieben <a href="https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/README.md">[3]</a>. Mit dem Tutorial der Cambridge University ist ein erster Anfang geschaffen, den ihr weiter ausbauen könnt und ein komplettes Betriebssystem schreiben könnt. Natürlich könnt ihr auch sowohl Assembler als auch eine höhere Sprache, wie C benutzen.</p>
<p>[1] <a href="http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/">http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/<br/>
</a>[2] <a href="http://www.heyrick.co.uk/armwiki/Main_Page">http://www.heyrick.co.uk/armwiki/Main_Page</a><br/>
[3] <a href="https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/README.md">https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/README.md</a></p>
<p>Folgendes Beispiel lässt ein SOS-Morsecode auf der ACT-LED blinken:</p>
<p>main.s</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="p">.</span><span class="n">section</span><span class="w"> </span><span class="p">.</span><span class="n">init</span></code></span>
<span class="code-line"><span class="p">.</span><span class="n">globl</span><span class="w"> </span><span class="n">_start</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="nl">_start</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="cm">/* enabling the ACT-Pin */</span></span>
<span class="code-line"><span class="w"> </span><span class="n">bl</span><span class="w"> </span><span class="n">gpio_address</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">16</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">r1</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">1</span></span>
<span class="code-line"><span class="w"> </span><span class="n">bl</span><span class="w"> </span><span class="n">gpio_pinmode</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// loading the pattern into r3, mask into r2</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">r5</span><span class="p">,</span><span class="w"> </span><span class="o">=</span><span class="n">pattern</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">r5</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="n">r5</span><span class="p">]</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">r6</span><span class="p">,</span><span class="w"> </span><span class="o">=</span><span class="mh">0x80000000</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="nl">blinkloop</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="cm">/* sets or resets according to the current pattern-value */</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">16</span></span>
<span class="code-line"><span class="w"> </span><span class="n">and</span><span class="w"> </span><span class="n">r1</span><span class="p">,</span><span class="w"> </span><span class="n">r5</span><span class="p">,</span><span class="w"> </span><span class="n">r6</span></span>
<span class="code-line"><span class="w"> </span><span class="n">bl</span><span class="w"> </span><span class="n">gpio_write</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="o">=</span><span class="mi">250000</span></span>
<span class="code-line"><span class="w"> </span><span class="n">bl</span><span class="w"> </span><span class="n">delay_ms</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ror</span><span class="w"> </span><span class="n">r6</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">1</span></span>
<span class="code-line"><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">blinkloop</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="p">.</span><span class="n">section</span><span class="w"> </span><span class="p">.</span><span class="n">data</span></span>
<span class="code-line"><span class="c1">// ldr works only if the data is set on an address multiple of 4</span></span>
<span class="code-line"><span class="p">.</span><span class="n">align</span><span class="w"> </span><span class="mi">2</span></span>
<span class="code-line"><span class="nl">pattern</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="p">.</span><span class="kt">int</span><span class="w"> </span><span class="mb">0b10010010111001110011100100100100</span></span>
<span class="code-line"></span></pre></div>
<p>gpio.s</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="p">.</span><span class="n">globl</span><span class="w"> </span><span class="n">wait</span></code></span>
<span class="code-line"><span class="p">.</span><span class="n">globl</span><span class="w"> </span><span class="n">gpio_address</span></span>
<span class="code-line"><span class="p">.</span><span class="n">globl</span><span class="w"> </span><span class="n">gpio_write</span></span>
<span class="code-line"><span class="p">.</span><span class="n">globl</span><span class="w"> </span><span class="n">gpio_pinmode</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="cm">/**</span></span>
<span class="code-line"><span class="cm"> * \brief waits for about r0 seconds</span></span>
<span class="code-line"><span class="cm"> * \param r0 amount of time the pi should wait (in seconds)</span></span>
<span class="code-line"><span class="cm"> * \return NONE</span></span>
<span class="code-line"><span class="cm"> **/</span></span>
<span class="code-line"><span class="nl">wait</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="n">cmp</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">0</span></span>
<span class="code-line"><span class="w"> </span><span class="n">beq</span><span class="w"> </span><span class="n">wait_back</span></span>
<span class="code-line"><span class="w"> </span><span class="n">sub</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">1</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mh">0x800000</span></span>
<span class="code-line"><span class="nl">innerloop</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="n">sub</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="err">#</span><span class="mi">1</span></span>
<span class="code-line"><span class="w"> </span><span class="n">cmp</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="err">#</span><span class="mi">0</span></span>
<span class="code-line"><span class="w"> </span><span class="n">bne</span><span class="w"> </span><span class="n">innerloop</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">wait</span></span>
<span class="code-line"><span class="nl">wait_back</span><span class="p">:</span><span class="w"> </span></span>
<span class="code-line"><span class="w"> </span><span class="cm">/* jump back */</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">lr</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="cm">/********************/</span></span>
<span class="code-line"><span class="cm">/** GPIO - SECTION **/</span></span>
<span class="code-line"><span class="cm">/********************/</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="cm">/**</span></span>
<span class="code-line"><span class="cm"> * \brief returns the GPIO-base address in r0</span></span>
<span class="code-line"><span class="cm"> * \return r0 GPIO-base address</span></span>
<span class="code-line"><span class="cm"> **/</span></span>
<span class="code-line"><span class="nl">gpio_address</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="o">=</span><span class="mh">0x20200000</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">lr</span><span class="p">;</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="cm">/**</span></span>
<span class="code-line"><span class="cm"> * \brief writes a value to the GPIO-Pin (i.e. writes 1 to set or 1 to clear)</span></span>
<span class="code-line"><span class="cm"> * \param r0 GPIO-Pin</span></span>
<span class="code-line"><span class="cm"> * \param r1 output-value (1 for on, 0 for off)</span></span>
<span class="code-line"><span class="cm"> **/</span></span>
<span class="code-line"><span class="nl">gpio_write</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="n">cmp</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">53</span></span>
<span class="code-line"><span class="w"> </span><span class="n">movhi</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">lr</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">//cmp r1, #1</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">//movhi pc, lr</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="n">r0</span></span>
<span class="code-line"><span class="w"> </span><span class="n">push</span><span class="w"> </span><span class="p">{</span><span class="n">lr</span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="n">bl</span><span class="w"> </span><span class="n">gpio_address</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// r0 -> GPIO-base address</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// r1 -> output value</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// r2 -> GPIO-Pin</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">//! still r1 is referenced here</span></span>
<span class="code-line"><span class="w"> </span><span class="n">cmp</span><span class="w"> </span><span class="n">r1</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">0</span></span>
<span class="code-line"><span class="w"> </span><span class="n">addhi</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">28</span></span>
<span class="code-line"><span class="w"> </span><span class="n">addls</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">40</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">cmp</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">31</span></span>
<span class="code-line"><span class="w"> </span><span class="n">subhi</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">32</span></span>
<span class="code-line"><span class="w"> </span><span class="n">addhi</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">4</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">r3</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">1</span></span>
<span class="code-line"><span class="w"> </span><span class="n">lsl</span><span class="w"> </span><span class="n">r3</span><span class="p">,</span><span class="w"> </span><span class="n">r2</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="n">r3</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="n">r0</span><span class="p">]</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="p">{</span><span class="n">pc</span><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="cm">/**</span></span>
<span class="code-line"><span class="cm"> * \brief sets the PIN into the mode we want</span></span>
<span class="code-line"><span class="cm"> * \param r0 GPIO-Pin</span></span>
<span class="code-line"><span class="cm"> * \param r1 GPIO-Mode</span></span>
<span class="code-line"><span class="cm"> * \return NONE</span></span>
<span class="code-line"><span class="cm"> **/</span></span>
<span class="code-line"><span class="nl">gpio_pinmode</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="cm">/** if (r0>53 || r1>7) return **/</span></span>
<span class="code-line"><span class="w"> </span><span class="n">cmp</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">53</span></span>
<span class="code-line"><span class="w"> </span><span class="n">cmpls</span><span class="w"> </span><span class="n">r1</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">7</span></span>
<span class="code-line"><span class="w"> </span><span class="n">movhi</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">lr</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">push</span><span class="w"> </span><span class="p">{</span><span class="n">lr</span><span class="p">};</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// save r0 (the pinnumber)</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="n">r0</span></span>
<span class="code-line"><span class="w"> </span><span class="n">bl</span><span class="w"> </span><span class="n">gpio_address</span><span class="p">;</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// r0 => GPIO_address</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// r1 => mode</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// r2 => pin</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="nl">pincalc</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="n">cmp</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">9</span></span>
<span class="code-line"><span class="w"> </span><span class="n">subhi</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">10</span></span>
<span class="code-line"><span class="w"> </span><span class="n">addhi</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">4</span></span>
<span class="code-line"><span class="w"> </span><span class="n">bhi</span><span class="w"> </span><span class="n">pincalc</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// r0 => correct address for the pin</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// r1 => mode (0-7)</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// r2 => pin-offset (0-9)</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// r2 = r2*3, because we need to shift r1 by r2</span></span>
<span class="code-line"><span class="w"> </span><span class="n">add</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="n">lsl</span><span class="w"> </span><span class="err">#</span><span class="mi">1</span></span>
<span class="code-line"><span class="w"> </span><span class="n">lsl</span><span class="w"> </span><span class="n">r1</span><span class="p">,</span><span class="w"> </span><span class="n">r2</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">push</span><span class="w"> </span><span class="p">{</span><span class="n">r4</span><span class="p">,</span><span class="n">r6</span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">r6</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">7</span><span class="w"> </span><span class="c1">// 000...00111</span></span>
<span class="code-line"><span class="w"> </span><span class="n">lsl</span><span class="w"> </span><span class="n">r6</span><span class="p">,</span><span class="w"> </span><span class="n">r2</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mvn</span><span class="w"> </span><span class="n">r6</span><span class="p">,</span><span class="w"> </span><span class="n">r6</span><span class="w"> </span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="c1">// get the currently set GPIO-register-values </span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">r4</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="n">r0</span><span class="p">,</span><span class="n">r2</span><span class="p">]</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// mask the according values to 0</span></span>
<span class="code-line"><span class="w"> </span><span class="n">and</span><span class="w"> </span><span class="n">r4</span><span class="p">,</span><span class="w"> </span><span class="n">r4</span><span class="p">,</span><span class="w"> </span><span class="n">r6</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// add the mode to the mode-vector</span></span>
<span class="code-line"><span class="w"> </span><span class="n">orr</span><span class="w"> </span><span class="n">r4</span><span class="p">,</span><span class="w"> </span><span class="n">r4</span><span class="p">,</span><span class="w"> </span><span class="n">r1</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// store the mode-vector back to the GPIO-memory</span></span>
<span class="code-line"><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="n">r4</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="n">r0</span><span class="p">]</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="p">{</span><span class="n">r4</span><span class="p">,</span><span class="n">r6</span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// return</span></span>
<span class="code-line"><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="p">{</span><span class="n">pc</span><span class="p">}</span></span>
<span class="code-line"></span></pre></div>
<p>timer.s</p>
<div class="highlight"><pre><span class="code-line"><span></span><code><span class="p">.</span><span class="n">globl</span><span class="w"> </span><span class="n">delay_ms</span></code></span>
<span class="code-line"><span class="p">.</span><span class="n">globl</span><span class="w"> </span><span class="n">delay</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="cm">/**</span></span>
<span class="code-line"><span class="cm"> * \brief returns the base address of the timer</span></span>
<span class="code-line"><span class="cm"> **/</span></span>
<span class="code-line"><span class="nl">counteraddr</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="o">=</span><span class="mh">0x20003000</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">lr</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="cm">/**</span></span>
<span class="code-line"><span class="cm"> * \brief waits for the given amount of seconds</span></span>
<span class="code-line"><span class="cm"> * \param r0 amount of seconds </span></span>
<span class="code-line"><span class="cm"> * \note is maybe not that exact :/</span></span>
<span class="code-line"><span class="cm"> **/</span></span>
<span class="code-line"><span class="nl">delay</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="n">push</span><span class="w"> </span><span class="p">{</span><span class="n">r5</span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">r5</span><span class="p">,</span><span class="w"> </span><span class="n">r0</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// using ldr, not 'mov r0, #1000000' because immediate</span></span>
<span class="code-line"><span class="w"> </span><span class="c1">// is not big enough to hold decimal 1000000</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="o">=</span><span class="mi">1000000</span></span>
<span class="code-line"><span class="nl">delay_inner</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="n">cmp</span><span class="w"> </span><span class="n">r5</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">0</span><span class="w"> </span></span>
<span class="code-line"><span class="w"> </span><span class="n">bls</span><span class="w"> </span><span class="n">delay_out</span><span class="p">;</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="w"> </span><span class="n">sub</span><span class="w"> </span><span class="n">r5</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">1</span></span>
<span class="code-line"><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">delay_ms</span></span>
<span class="code-line"><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">delay_inner</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="nl">delay_out</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="p">{</span><span class="n">r5</span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">pc</span><span class="p">,</span><span class="w"> </span><span class="n">lr</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="cm">/**</span></span>
<span class="code-line"><span class="cm"> * \brief waits for the given amount of microseconds</span></span>
<span class="code-line"><span class="cm"> * \param r0 amount of microseconds</span></span>
<span class="code-line"><span class="cm"> **/</span></span>
<span class="code-line"><span class="nl">delay_ms</span><span class="p">:</span></span>
<span class="code-line"><span class="w"> </span><span class="n">push</span><span class="w"> </span><span class="p">{</span><span class="n">lr</span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="n">push</span><span class="w"> </span><span class="p">{</span><span class="n">r6</span><span class="p">,</span><span class="n">r7</span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="n">mov</span><span class="w"> </span><span class="n">r4</span><span class="p">,</span><span class="w"> </span><span class="n">r0</span></span>
<span class="code-line"><span class="w"> </span><span class="n">bl</span><span class="w"> </span><span class="n">counteraddr</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">4</span><span class="p">]</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">r3</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">8</span><span class="p">]</span></span>
<span class="code-line"><span class="w"> </span><span class="n">add</span><span class="w"> </span><span class="n">r1</span><span class="p">,</span><span class="w"> </span><span class="n">r2</span><span class="p">,</span><span class="w"> </span><span class="n">r4</span></span>
<span class="code-line"><span class="w"> </span><span class="n">addcs</span><span class="w"> </span><span class="n">r3</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">1</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="nl">delay_ms_inner</span><span class="p">:</span><span class="w"> </span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">r6</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">4</span><span class="p">]</span></span>
<span class="code-line"><span class="w"> </span><span class="n">ldr</span><span class="w"> </span><span class="n">r7</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="n">r0</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="mi">8</span><span class="p">]</span></span>
<span class="code-line"><span class="w"> </span><span class="n">cmp</span><span class="w"> </span><span class="n">r1</span><span class="p">,</span><span class="w"> </span><span class="n">r6</span></span>
<span class="code-line"><span class="w"> </span><span class="n">cmpls</span><span class="w"> </span><span class="n">r3</span><span class="p">,</span><span class="w"> </span><span class="n">r7</span></span>
<span class="code-line"><span class="w"> </span><span class="n">bhi</span><span class="w"> </span><span class="n">delay_ms_inner</span></span>
<span class="code-line"><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="p">{</span><span class="n">r6</span><span class="p">,</span><span class="w"> </span><span class="n">r7</span><span class="p">}</span></span>
<span class="code-line"><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="p">{</span><span class="n">pc</span><span class="p">}</span></span>
<span class="code-line"></span></pre></div>