Commits

Anonymous committed 76d27c3

added ivm and nikl pages

Comments (0)

Files changed (3)

 <li>six - very simple IRC client [<a href="http://bitbucket.org/zserge/six">repo</a>]</li>
 <li>gotftp - basic TFTP server written in Go
 [<a href="http://bitbucket.org/zserge/gotftp">repo</a>]</li>
+<li><a href="nikl.html">nikl</a> - micro-framework for programming embedded systems [<a href="http://bitbucket.org/zserge/nikl">repo</a>]</li>
+<li><a href="ivm.html">ivm</a> - simple forth virtual machine (a part of <a href="nikl.html">nikl</a>) [<a href="http://bitbucket.org/zserge/nikl">repo</a>]</li>
 </ul>
 
 <h2>Contact</h2>
+<!DOCTYPE HTML> 
+<html lang=en>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>nikl</title>
+	<link rel="stylesheet" href="style.css" type="text/css"/>
+</head>
+<body>
+<div id="container">
+	<div id="content">
+<h1>IVM</h1>
+
+<p>IVM (IV Machine or Forth Machine) is a very simple virtual machine
+for small embedded devices. It's a stack machine and is designed
+to run Forth code.</p>
+
+<h2>GOALS</h2>
+
+<p>Imagine that you have a very simple hardware with complicated logic.  You could
+use tiny chip with a few peripherals, as you need just a couple of GPIOs and 
+maybe UART or SPI. But your software will never fit that 2K of ROM that you
+can find on the cheapest microcontrollers. The solution can be run only a virtual 
+machine on the micro, but store code on external storage, like EEPROM or SPI
+flash.</p>
+
+<p>Another situation. You need to update the firmware after the device is producted.
+You can write the firmware update module, and run it when, say, an SD card is plugged
+into the device. But, on the other hand, you can store the code on the external card
+as well, right?</p>
+
+<p>So, IVM is designed to be as small as possible (less than 1K) to fit even the smallest micros, and it
+should bring them to the new level by possibility of running external code.</p>
+
+<p>IVM should be flexible and easy to customize.</p>
+
+<h2>DESIGN</h2>
+
+<p>IVM code is stored in the "/inc/ivm.h" file. It has two functions:</p>
+
+<ul>
+<li>ivm_reset() - brings the VM back to the default state</li>
+<li>ivm_step(op) - executes instruction "op" on the virtual machine</li>
+</ul>
+
+<p>You need to implement some functionality by your own:</p>
+
+<ul>
+<li><p>fetching instructions. Each instruction is a 2-byte word. Instruction 
+index is stored in the "pc" variable</p></li>
+<li><p>implementing memory. You must implement ivm<em>mem</em>put and ivm<em>mem</em>get functions
+to read and write to the memory. It is not a part of IVM code, because you can
+use your I/O ports in a memory mapped way, or you might want to cache some
+addresses if you use external memory chip. Also, you decide what is the amount
+of RAM available to the VM.</p></li>
+</ul>
+
+<h2>INSTRUCTION SET</h2>
+
+<p>IVM has a simple assebler compatible with J1 chip.</p>
+
+<p>IVM has two stacks - data stack (DS) and return stack (RS).
+Default depth for both of them is 16 levels.</p>
+
+<p>Basically, there is 5 types of the instructions:</p>
+
+<ul>
+<li><p>LIT: put a number on the DS</p></li>
+<li><p>JMP: jump to address</p></li>
+<li><p>JZ: jump to address if the value on the top of the DS is zero. 
+This instruction also deletes this value from the top of the DS.</p></li>
+<li><p>CALL: store current address to the RS and jump to the address.
+This instruction makes it possible to implement functions.</p></li>
+<li><p>ALU: perform ariphmetic operation</p></li>
+</ul>
+
+<p>There is 16 types of ALU instructions, each of them can also manipulate
+DS and RS stacks, change PC and work with memory. For more details
+check the J1 project.</p>
+
+<h2>COMPILER</h2>
+
+<p>If you know Forth, you can use crosscompiler from J1 project.</p>
+
+<p>There is a simple compiler written in Python (check utils/ivmc).
+It's not actually forth compiler, but is very similar.</p>
+
+<p>To compile your program you need to:</p>
+
+<pre><code>$ ivmc file1.fs [file2.fs file2.fs ... ] rom.bin
+</code></pre>
+
+<p>This will create a binary file with is a IVM rom. Now you can
+put it on the external storage and run with your virtual machine.</p>
+
+<p>Files will be compiled in the order you write their names, so file2.fs
+can use functions from file1.fs.</p>
+
+<h2>IVM FORTH-LIKE DIALECT</h2>
+
+<p>Source file is a list of space-separated words. Each words is a literal
+(number, letter) or a function.</p>
+
+<p>Forth uses postfix notation. </p>
+
+<p>IVM dialect is specific, because has inline macros.</p>
+
+<p>Macro starts with '::' and ends with ';;' words.
+When macro is called somewhere in your code it is not replaced with a jump or call,
+like a normal funtion, but is inserted in your code, e.g:</p>
+
+<pre><code>...
+: inc ( x -- x+1 ) 1 + ;
+2 inc
+</code></pre>
+
+<p>It will give code:</p>
+
+<pre><code>Function code:
+0x100: LIT 1
+0x102: ALU_PLUS
+0x104: RETURN
+
+Caller code:
+0x105: LIT 2
+0x106: CALL 0x100
+</code></pre>
+
+<p>If you rewrite it as a macro</p>
+
+<pre><code>:: inc ( x -- x+1 ) 1 + ;;
+2 inc
+</code></pre>
+
+<p>You will get:</p>
+
+<pre><code>0x100: LIT 2
+0x102: LIT 1
+0x104: ALU_PLUS
+</code></pre>
+
+<p>Macros are useful to define variables. Memory operators are '!' and '@', as in forth:</p>
+
+<pre><code>2 5 ! ( put 2 at address 0x5 )
+3 @ ( read from 0x3 to the top of DS )
+</code></pre>
+
+<p>Not to hardcode memory addresses, use macros (with no performance or size affects):</p>
+
+<pre><code>:: x 0 ;;
+:: y 1 ;;
+
+2 x ! ( write 2 to x ) y ! ( read from y )
+</code></pre>
+
+<p>There is a base library (base.fs) with macros to use in your code.</p>
+	</div>
+</div>
+</body>
+</html>
+<!DOCTYPE HTML> 
+<html lang=en>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+	<title>nikl</title>
+	<link rel="stylesheet" href="style.css" type="text/css"/>
+</head>
+<body>
+<div id="container">
+	<div id="content">
+<h1>nikl</h1>
+
+<p>Nikl is a micro-framework for small embedded devices like AVR
+microcontrollers.</p>
+
+<h2>HISTORY</h2>
+
+<p>One day I decided to write a real-time OS. It had multitasking, semaphores and
+supported only ARM architecture. Then I tried to port it to use with the AVRs,
+but context switching was slow, memory was very limited and my OS became
+generally useless.</p>
+
+<p>Then, I started another project called avOS, that some kind of a kernel that
+was not real-time, and was widely using protothreads for pseudo-multitasking.
+Later the project was abandoned, but now I'm trying to give it a new born.</p>
+
+<h2>PHILOSOPHY</h2>
+
+<ul>
+<li>No .c files, until they are really needed</li>
+<li>This implies - no build system. You can take any compinations of modules you
+need and just copy or symlink them to your project directory</li>
+</ul>
+
+<h2>STRUCTURE</h2>
+
+<p>You should see "/inc" folder. There is where all the modules are stored.
+Another folder is "/test". This is a folder with unit tests. </p>
+
+<p>Let's go deeper. Inside "/inc" folder you should find modules for</p>
+
+<ul>
+<li>bare-bone unit-testing (test.h)</li>
+<li>list-based data structures (list.h)</li>
+<li>FORTH virtual machine (ivm.h)</li>
+</ul>
+
+<p>For the IVM there is a simple comiler written in Python (see "/utils/ivmc").</p>
+
+<h2>HOW TO USE TESTS</h2>
+
+<p>There is a bunch of unit-testing frameworks. But they all are an overkill for
+tiny embedded systems. If you just need to write a few tests for your device
+and have a possibility to detect which test failed - you can use test.h.</p>
+
+<p>Here's an example of typical usage. Assume we have a board that works with data
+on the SD card. We need to be sure that SD card is valid, and hardware is ready
+to use.  I still doubt if it is a correct usage of unit tests, as it seems like
+we test hardware, but this is how it works for me:</p>
+
+<pre><code>/* Use custom error notification, e.g. show line number on LCD,
+ * or send message and line number over UART, or just turn the
+ * error indicating LED on */
+#define TEST_NOTIFY(msg, line)  lcd_display_number(line)
+
+#include &lt;test.h&gt;
+
+/* Check that after hardware initialization SD card is present */
+int test_sdcard_available() {
+    int part_id;
+    sdcard_init();
+    check(sdcard_available() == 1);
+
+    /* Ensure that FAT partition ID is 1..4 */
+    part_id = sdcard_find_fat_partition();
+    check(part_id &gt; 0 &amp;&amp; part_id &lt;= 4);
+
+    return 0;
+}
+
+/* Check that toggling LED port works correctly */
+int test_leds() {
+    LED_PORT |= (1 &lt;&lt; GREEN_LED);
+    check(LED_PORT &amp; (1 &lt;&lt; GREEN_LED));
+    LED_PORT &amp;= ~(1 &lt;&lt; GREEN_LED);
+    check((LED_PORT &amp; ~(1 &lt;&lt; GREEN_LED)) == 0);
+}
+
+....
+
+test(test_sdcard_available(), "sdcard test");
+test(test_leds(), "LED test");
+
+...
+</code></pre>
+
+<p>Ain't it simple? You basically write test functions that do some assertions.
+Then you call these functions and if the return value is non-zero - it means
+that the test has failed. In this case TEST_NOTIFY function will be called and
+it's your chance to tell user that something was wrong</p>
+
+<ul>
+<li>your test functions must return 0 at the end to indicate success</li>
+<li>you need to test values using check(condition) macro.</li>
+<li>you need to run your test functions using test(func, descr) function</li>
+<li>if you use your own TEST_NOTIFY implementation you can print test name
+(msg argument) and line number (line argument), where the error occurred.</li>
+</ul>
+
+<h2>HOW TO USE LISTS</h2>
+
+<p>Lists are stolen from linux kernel in a shameless way. They are not so easy-to-use
+as more common <sys/queue.h>, but are more flexible.</p>
+
+<p>To define an object, that can be linked into the list, do:</p>
+
+<pre><code>typedef struct {
+    /* My data */
+    int value1;
+    int value2;
+    /* List pointer */
+    list_t list;
+} my_obj;
+</code></pre>
+
+<p>To create a list head, do:</p>
+
+<pre><code>list_t my_obj_list;
+</code></pre>
+
+<p>Next, initialize list:</p>
+
+<pre><code>list_init(&amp;my_obj_list);
+</code></pre>
+
+<p>And now you can do everything you want with your lists:</p>
+
+<pre><code>my_obj *a, *b, *c; /* object to add to the list */
+list_t *tmp; /* list iterator */
+
+list_add(&amp;my_obj_list, &amp;a-&gt;list);
+list_add(&amp;my_obj_list, &amp;b-&gt;list);
+list_add(&amp;my_obj_list, &amp;c-&gt;list);
+
+list_del(&amp;b-&gt;list);
+
+list_for_each(&amp;my_obj_list, tmp) {
+    /* This is how to get the object using list_entry() */
+    my_obj *iterator;
+    iterator = list_entry(tmp, my_obj, list);
+    printf("%d\n", iterator-&gt;value1);
+}
+</code></pre>
+
+<p>For now only double-linked lists are implemented. Soon single-linked lists and
+queues will be added.</p>
+
+<h2>HOW TO USE IVM</h2>
+
+<p>IVM is not that simple. See IVM.README for more details to how use it.</p>
+	</div>
+</div>
+</body>
+</html>