The Kestrel-2DX, a specific embodiment of the Kestrel Computer Project (also on Hackaday), will help with new hardware bring-up.

Similar projects worth following
I've had many problems getting a working Kestrel-3 design. I have the CPU, I have a bunch of useful cores, but it seems bringing up external RAM resources proved impossibly difficult for me. The now obsolete Nexys-2 FPGA development board uses an obstinate pseudo-SDRAM chip which neither functioned correctly in static RAM nor in synchronous dynamic RAM modes. This basically killed the project cold.

After taking about a year off from the project, I've decided to just revisit what I know works: the Kestrel-2. I wanted a computer design that I could basically start out as a Kestrel-2, then incrementally add features to to finally realize my vision of a Kestrel-3.

The first step in this journey is to replace the old S16X4A CPU, a 16-bit CPU which could only address 64KB of memory, with the same CPU that will eventually power the Kestrel-3. This is that computer design.

This sub-project exists for the benefit of the 2017 Hackaday Prize contest.

How will the Kestrel-2DX be world changing?

By letting me bring the Kestrel-3 into existence, it will be an instrumental in helping me to fight such nonsense as motherboards with EULAs, the industry's inexorable march towards appliance culture, and thus restoring hackability to the computer's owners.

You can read more about the technical, social, and political motivations behind the Kestrel Computer Project on the official page or on my main Hackaday KCP project page. 

OK, so how will the Kestrel-2DX help bring up the Kestrel-3?

In two important ways, actually.

First, and foremost, it'll make viable workbench computer.  Like a Commodore PET or TRS-80, the 2DX will have directly accessible I/O ports.  This will allow it to interact with its environment without a bunch of fuss.  You might think, "Yeah, but an Arduino can do that!"  However, Arduino-based designs usually doesn't have a keyboard and monitor with which a user can directly interact.  If they did, it'd consume their entire set of resources.  Alternatively, you'd need to interface to it with a bunch of USB-to-whatever adapter cables, which I'm sorry, is just a pain in the posterior.  These adapters are relatively expensive (as I write this, USB-to-RS232 cables average 5x the cost of a typical ESP8266 module with USB cable) and constantly missing when you need them most.

The Kestrel-2DX fills a nice niche here: I intend to program it interactively to serve various bring-up and monitoring functions as I'm raising the Kestrel-3.  This is not unheard of in computing history: the VIC-20 and Commodore 64 were both brought up with the help of Commodore PETs; Atari Jaguar was brought up with the help of Atari STs, etc.

Second, the FPGA design files themselves can be used as a foundation for the Kestrel-3.  As the project description indicated above, the new set of design files are designed to make evolution much easier.  Switching the old, stack-based CPU out for the 64-bit RISC-V CPU is the first step in making this transition.  It's also, relatively speaking, the most expensive and riskiest.


Kestrel-2DX booting into an early prototype of DX-Forth. Kestrel-2DX is synthesized onto a Digilent Nexys-2, and is booting a 4KB (8 sectors) binary image into memory address $14200 off of SD-card. SD card access is bit-banged from the CPU.

MPEG-4 Video - 5.65 MB - 12/22/2017 at 03:45


  • 1 × Digilent Nexys-2 FPGA Development Board. DISCLAIMER: These are no longer being manufactured.
  • 1 × PS/2 Keyboard of your choice.
  • 1 × VGA-compatible monitor of your choice.
  • 1 × FPGA development workstation PC. This will also be used for software development too.

  • It's Done. I'm Finished. Stick a Fork In It. Computer is Served!

    Samuel A. Falvo II02/24/2018 at 15:56 1 comment

    I'm throwing in the towel once more on the Kestrel-2DX project.  However, not because I'm aggravated, or feel defeated over some seemingly insurmountable technical issue (*cough!* Pseudo-SDRAM *cough!*).  Oh no -- this is far, far better than that.  I'm throwing in the towel because I've won.  The Kestrel-2DX, as I've come to envision the computer design, is complete.

    This computer is, bugs and/or feature requests notwithstanding, fully operational.

    For the last two weeks, I have spent zero time using my workstation PC for the purposes of Kestrel development.  My entire interaction with the Kestrel, backups notwithstanding, has been with developing software directly on the Kestrel, inside of DX-Forth.  It's even largely the reason why I haven't been making updates as of late.

    This has lead me to what I think is perhaps my first Law of Computing:

    You know you're finished when you spend more time with your project than you do with the tools to build it.

    I've been having a blast.  I spent the last week or so building up a set of slides to present at today's SVFIG meeting.  Despite DX-Forth not being a super-high performer, I'm planning writing a simple, terribly elementary game -- you know, the sorts of games one would write in BASIC on a Commodore 64 or Apple II.  I doubt it'll push its limits; but, it's all that I know how to do, and it should show nicely what is possible to accomplish with the machine to others.  I'm considering creating some videos along the way as well, so folks can see what it's like working with the system in real-time. 

  • DX-Forth Prints Numbers, Stack

    Samuel A. Falvo II01/08/2018 at 17:53 0 comments

    Just a quick update before I partake in more celebratory shenanigans.  I managed to implement . and U. in DX-Forth, so now I can print numbers.  Unlike most Forths, these words do not append a space to the numbers they print; I'm not convinced it will remain this way forever, but for now, it's convenient.  Using these words, I implemented .S to dump the contents of the stack as well.

    Now that I have these features implemented, my next step is to implement block storage.  I intend on having four block buffers, and will implement a simple command-line text editor lexicon.  (I do not think I have enough room to implement a clone of VIBE.)  Only after I implement the block storage system will I consider starting to develop the colon compiler.

    I have a fur-suiting convention to attend this weekend, and a family get-together the weekend following, so I do not expect to make further progress until at least February.

  • Kestrel-2DX Booting a Prototype Forth Environment

    Samuel A. Falvo II12/22/2017 at 01:01 0 comments

    Apologies for the lengthy update delay; life intervened as usual, and with the Hackaday contest over, I didn't put as much priority on releasing updates.

    But, I was not idle!  As this video shows, the Kestrel-2DX is live and is now booting arbitrary binary blobs.

    The computer is coming out of hard reset; it's actually waiting for input before the monitor comes up with a video display.  Once I have a TIM/V prompt, I use it to initialize the SD card, then to load the binary image into RAM, then to jump into it.

    The image loaded is 4KB in size, and is the beginnings of a Forth interpreter made just for the Kestrel-2DX.  (The old Kestrel-3 eForth port won't work on the 2DX because it doesn't have enough memory for it.)

    The Forth interpreter is not complete: literally, it only supports small decimal numbers and the ! operation.  It's got a long ways to go before supporting compilation.  Still, this is a positive development!  I'm quite excited!

  • Full Screen Editor-based Operating System

    Samuel A. Falvo II10/16/2017 at 06:54 0 comments

    This past week, I've been working on an experimental piece of software that I hope will be relatively easy to port to the Kestrel-2DX.  It's an operating system, but unlike Unix and much more like IBM mainframes, it's based on the concept of presenting a user with a set of editable fields on "panels", which are presented to the user a full screen at a time.  Any 3270 terminal user of an IBM mainframe would feel quite at home using it.

    Thankfully, the programming model is substantially different, and easier to get working.  ;)

    For expediency, I wrote the first major version of this environment using GForth 0.7.0 on a Linux environment.  I am currently using this environment to write a set of filesystem utilities (e.g., format volume, allocate file, etc.), which accomplishes two goals simultaneously:

    I can work out the kinks of using the FSE environment, so when it comes time to port the software, I can do it the right way, and,

    I can build up a library of code to manipulate Kestrel-2 SL-5 volumes.  As some may remember, SL-5 filesystems came into existence with the introduction of STS, my operating system for the S16X4-based, 16-bit Kestrel-2.  This time, I intend on fixing some long-standing problems with SL-5 (e.g., each file having only one extent).

    The software is written in ANS Forth for the moment; however, it should be relatively easy to target-compile into RISC-V assembly language when the time comes.  It'd be nice to have the same basic set of routines working on both my Linux PC and the Kestrel-2DX.  :)

  • COM in BIOS? Not Anymore.

    Samuel A. Falvo II10/16/2017 at 05:14 0 comments

    The Problem

    Well, that was a fun experiment, and I managed to get everything working; but, I had to remove COM from the BIOS.  The problem was that, with MGIA video buffer spanning $10000-$13E7F, I only have 384 bytes for the BIOS to use between $13E80-$13FFF.  I don't nest subroutines very deeply, so you'd expect this to be plenty of space, even with GCC, which requires a stack pointer to be aligned on a 16-byte boundary at all times.

    Well, it turns out that, for some reason, COM method calls resulted in a bit too much nesting, as more pressure was placed on the stack to hold such things as method table pointers and such.  The result is that the BIOS would corrupt the video display.  Don't get me wrong: nothing ever crashed (though it could have under some pathological cases); but it was a very obvious bug that needed repair.

    Most video bugs are, I guess, "very obvious bugs" by definition.  ;)

    The Resolution

    Instead of COM, I just use a flat entry-point vector (table of function pointers).  Programs loaded from secondary storage have zero idea where this table is actually located, so it must scan memory for a special signature to locate it.  It's hokey and hackey, but it works, and allows me the freedom to relocate where I load programs in memory at any time in the future.  The only hard requirement I have in place currently is that the signature must reside in the first 128KiB of the CPU's address space.

    This more direct approach to linking functionality from ROM into RAM has reduced stack pressure enough that the video display is no longer corrupted during normal operation of the TIM/V monitor.

    Lessons Learned

    384 bytes is a relatively small amount of stack space, especially for software compiled with GCC.  Point blank, C is not a good language to write small, tight, highly efficient software in.  The problem is not code size (at least not directly); rather it's how it uses its stack.  Overloading a single stack with both continuation and state information results in a larger than expected pressure on the stack, for two reasons:

    1. The compiler must generate stack frame constructors and destructors for each procedure you compile.  If all you're doing is threading values from one function to another three call levels down, the two intermediate procedures must include code to shuttle data to and from: wasted instructions and stack space that otherwise serves no useful purpose.  In COM, this happens with surprising frequency.  With a dual-stack environment, this never happens.  The result is a reduction of code you need to run, as well as a reduced stack frame size, without sacrificing proper code structure.
    2. Second, Forth-like languages often make use of two (or more) small stacks, not one big stack.  Separating data from continuation information makes it vastly easier to recycle the relevant stack space.  It's not uncommon for very small Forth systems to have between 8 and 16 slots on their data and/or return stacks.  Phil Koopman, in his book Stack Computers, The New Wave, documents how incredibly rare it can be for software to exceed 24, 16 slots on their data, return stacks (respectively).  Packing a Forth runtime environment's stacks into 384 bytes would be an exercise in triviality.

    I'd like to rewrite the software in a static subset of Forth, using a compiler specially design for the Kestrel-2DX's unique memory requirements.  This means it should be quite miserly with its use of stack space in practice.  Obviously, I have something which works now; I've not decided if I will go forward with the Forth compiler idea yet.

    In either case, however, software bootstrapped from secondary storage is expected to relocate the stack, so the 384 bytes configured at system start-up time is not set in stone.

  • Component Object Model for a BIOS?! Why, yes!

    Samuel A. Falvo II09/29/2017 at 16:04 0 comments

    Now that I have SD card access working, I find myself in a position where I must now consider how to invoke BIOS services from programs developed long after BIOS itself has been compiled.  I don't want to have to resort to using the CPU's ECALL or EBREAK instructions (or any trap, for that matter), because this both consumes an opcode that should really be used by a proper operating system, and because it would require that I renovate the linkage approach I have between the assembly language bootstrap and the C code.  Jump-tables or entry-point vectors are ideal for the needs of the BIOS; since I am working primarily in C at the moment, it seems reasonable that invoking services through a C-accessible mechanism is the best solution (otherwise, I'd have to write yet more assembly language stubs/proxies).  I knew just where to look; I decided to go back a decade, and excavate my old code from my GCOM project, my own clean-room clone of Component Object Model.

    I'm happy to announce that I've managed to port a reasonable subset of GCOM into the firmware.  As the proof of concept, I only implemented console output functions and basic cursor control.  Keyboard input and SD card access will come later as time permits.

    Read more »

  • TIM/V: The Terminal Interface Monitor for RISC-V

    Samuel A. Falvo II09/24/2017 at 16:48 0 comments

    A double-whammy of good news today!  First, I'll talk about TIM/V, then I'll segue into SD card support.


    If you're reading my progress, you're probably also familiar with the Commodore PET series of computers.  If you've ever used them, you might also remember the built-in machine language monitor.  You could break into the monitor at any time from BASIC with a SYS 4 command.  This monitor's name was never printed to the screen; but it's official name is TIM, the Terminal Interface Monitor.  Its heritage actually goes back to the KIM-1 computer!

    Well, today[*], I'm happy to announce that my Kestrel-2DX computer has its own port of TIM, which I've named TIM/V.  For expediency, I wrote it in C, and cannot yet do everything the real TIM can do.  Part of the reason for this is limitations I'm running into as software size in ROM grows.

    TIM/V does, however, have the minimum number of features required to provide self-hosted software development on the Kestrel-2DX.  No, they're not convenient to use, particularly if you're pampered by today's software development environments.  But it does work, and with enough patience and practice, it can be used to write literally anything that can run on the Kestrel-2DX.

    TIM/V supports the following features:

    • You can use the m [addr1][,addr2] command to inspect the contents of memory.
    • You can use the = addr1 byte byte byte ... command to set the contents of memory.
    • You can use the g [addr1] command to "goto" (actually, call) an arbitrary machine language subroutine.
    • You can use the l addr1,blk-start,blk-len command to load a binary image into memory from an initialized SD/MMC card.
    • You can use the s addr1,blk-start,blk-len command to save a binary image back to your SD/MMC card.
    • You can use the i command to initialize the most recently inserted SD/MMC card.

    There's no unmount command, so you can insert and remove cards arbitrarily.  In fact, since the SD card interface lacks power control (not enough I/Os in the PMOD and no driver circuitry on the adapter circuit), if ever the SD/MMC card gets into a weird state, the only way to recover is literally to pull it out and re-insert it.  This manually cycles power to the card.  Remember to follow up with an i command to reinitialize it.

    As of today, no commands exist to dump or set the contents of CPU registers, and certainly no support for breakpoints or watch-points exist yet either.  These may come later after I've solved some higher priority issues/tasks.

    Using TIM/V as a Boot Loader

    As it happens, you can use TIM/V as a primitive boot loader.  Assuming you just inserted an SD/MMC card into the slot, you can boot the computer with the following sequence of commands:

    1. i
    2. l 1bc00,0,2
    3. g 1bc00

    The 2nd command tells TIM/V to load a binary image from SD/MMC starting at sector 0, for two sectors (so, 1KiB), and place the data at address $1BC00 in IPL RAM.  The 3rd command, obviously, then executes the loaded code.

    SD/MMC Card Support

    Like the original Kestrel-2, the 2DX only supports a single SD/MMC card.  It's all I have, sorry.  I have future plans to expand this with the Kestrel-3; but, for now, it's bit-banged via the GPIA and only a single slave-select pin is mapped.

    That said, since its inception six years ago, the Kestrel-2 has never been able to write to an SD/MMC card.  There just wasn't enough space in the bootstrap code to include it!  Thankfully, now that the 2DX isolates ROM from RAM, and ROM is truly permanent, write support now exists!

    This means that any operating system I implement for the 2DX will properly be able to save work artifacts.  (With the original Kestrel-2, I was intending on including write-support in a code overlay library, but never got that far.)

    SDHC Support Still Lacking

    Alas, the 2DX remains constrained to using normal SD cards (it's not tested with a proper MMC card).  None of the SDHC cards I have seem to initialize...

    Read more »

  • BIOS Update and Plans

    Samuel A. Falvo II09/13/2017 at 16:22 0 comments

    A quickie, since I'm getting ready for work as I type this.

    Status Update

    The Kestrel-2DX BIOS now has ASCII-capable keyboard functionality.  So, with character output and keyboard input now defined, I basically have a really dumb terminal at my disposal.

    I need to wrap the functionality up into interfaces that are more convenient to use, however.  For example, console output requires that you manually turn the cursor off before printing or moving the cursor to a new location on the screen, then turn it back on again afterwards (worry not; cursor_off() and cursor_on() properly nest up to 65535 levels deep).  Likewise, keyboard input is handled using a kind of polling interface that would suit an evented software stack quite well, but is awkward for Unix-style interfaces.

    So far, the software consumes 3688 bytes of ROM space, with another 3200 bytes consumed by PS/2-to-ASCII translation tables and the 8x8 fixed-width font, for a total space occupation of 6888 bytes as of this log post.

    Future Plans/Ideas

    Looking forward, I'm seriously thinking about migrating the design of the bootstrap firmware towards an event-driven architecture.  The code as written seems already well suited to this architecture.  In fact, in my dummy integration test code currently in ROM (which will undeniably change by the time you read this, as progress continues), there is an event loop which illustrates how to properly poll for keyboard input:

    static void
    ps2_playground(void) {
    	uint16_t rawkey, code;
    	int valid;
    	char ascii;
    	int shifted = 0, ctrled = 0;
    	for(;;) {
    			(uint16_t *)(&rawkey),
    			(int *)(&valid)
    		if(valid) {
    			code = rawkey & 0x7FFF;
    			if((code == RAWKEY_CTRL_L) || (code == RAWKEY_CTRL_R)) {
    				ctrled = (rawkey & RAWKEYF_RELEASE) == 0;
    			if((code == RAWKEY_SHIFT_L) || (code == RAWKEY_SHIFT_R)) {
    				shifted = (rawkey & RAWKEYF_RELEASE) == 0;
    			kia_raw_to_ascii(rawkey, shifted, ctrled, &ascii, &valid);
    			if(valid) {

    I have written keyboard code many times over the years (Kestrel-2 emulator, Kestrel-2 FPGA, Kestrel-3 emulator, etc.), and none have been so compact as this, especially considering how trivial it is to maintain shift state (required for proper ASCII conversion).  Funny to think that this code is not designed; it's not even unit tested.  It just fell out of the design naturally, and "just worked."

    So, for the purposes of writing the first interactive shell for the Kestrel-2DX, I think the only things I need to work on is:

    • a centralized BIOS event polling and dispatching function,
    • a mechanism for installing callbacks for said events, and,
    • a mechanism for invoking BIOS functions from programs loaded into RAM.

    The another nice advantage of building a BIOS in an event-driven manner like this, which I believe I touched upon in an earlier blog post on the main Kestrel Computer Project page, is that it completely isolates the event handlers from whether or not I choose to use interrupts.  Currently, despite the CPU supporting them, I make no use of interrupts.  But, if/when I add timer support, interrupts will need to be supported.  Further, longer term, when I support SD card I/O, initial implementations will not use DMA; but I do intend on using DMA eventually, which again requires interrupts.  The programming interface should remain invariant under these changing conditions.

    I know from experience that making the KIA interrupt-driven fundamentally alters how to write a keyboard driver and, if you insist on a Unix-style synchronous I/O model, not in a pleasant way.  You end up needing all manner of additional impedance matching constructs like FIFOs, and you need to worry about race conditions between interrupt handlers and FIFO consumers, etc.  Although not technically multi-threaded, you suddenly need to think in multi-threaded terms.  From there, it's not much...

    Read more »

  • More Detail: How I Intend K2DX to Help Bring Up K3

    Samuel A. Falvo II09/11/2017 at 17:54 1 comment

    Once I get the Kestrel-2DX to a point where I can interact with it and program it live, I intend on starting work on bringing up the Kestrel-3.  But, to do this, I need access to external RAM chips, so I can play with more than 24K to 48K of memory.

    Port Kestrel-2DX to Icoboard Gamma/MyStorm Black Ice

    Everything discussed herein applies equally to the Icoboard Gamma or to the MyStorm Black Ice.  Both have an iCE40HX8K-compatible FPGA on board, and are equipped (or can be be so equipped) with 1MB of externally accessible SRAM.  I'll refer to these boards as "the target" for brevity.

    Perhaps one of the first things I can do is port the Kestrel-2DX design files to the target FPGA development board.  Alas, both of the target boards lack human-interface I/O ports like VGA or PS/2 port, so for now, we need a simpler I/O approach.

    My plan here is to use my Nexys-2 as a terminal for the Icoboard Gamma implementation.  Thanks to both systems having at least one available, 3.3V, 4-bit PMOD connector, my current train of thought is to implement my SIA core on each of them, and get them both to talk to each other over a synchronous serial interconnect.  I should be able to accomplish a link by stringing over just five wires.

    The Nexys-2 would consume PS/2 keyboard input and serialize it for the Icoboard; and, vice versa, it would serve as a remote frame buffer for display output purposes.  This kind of channel requires intelligence on either side of the link, of course, which means we could load additional telemetry functionality onto the link as well.

    System software changes will be necessary to take advantage of this serial interconnect, of course.  I'm not quite sure how to implement these yet, but I have some nebulous ideas.  I'll discuss these later as time permits.

    Beyond the Icoboard Gamma/Black Ice Boards

    Once I have a remote system up, and have a working, interactive environment there, I can attempt to work on the CGIA video interface (Configurable Graphics Interface Adapter).  The CGIA is the MGIA's replacement, supporting features such as color, higher resolutions, and other features one would expect of a contemporary computer.  First generations of development won't have any blitters or GPUs; sorry.  For those old enough to remember them, you can probably get a good mental image in your head of the CGIA's capabilities by comparing it to the Atari ST 520/1040's "shifter" video chip.

    Once again, system software will require retrofitting to take advantage of the CGIA.  I anticipate this should be rather simple; remember that the step prior required us to route bitmapped data over a serial interconnect, so by this point, acceptable video primitives and abstractions should exist in a state which can make this relatively easy to do.

    After getting the CGIA-based system software running, self-hosted software development should then be possible (well, of course, after porting compilers and other development tools).  This would probably be my first "minimum viable" Kestrel-3.  If I can be so lucky as to port Fossil SCM over to the Kestrel-3 environment, that would totally be icing on the cake!

  • KIA Integrated.

    Samuel A. Falvo II09/05/2017 at 04:52 0 comments

    Well, OK, that went a whole lot faster than I anticipated.  :)

    I guess my next step from here is to write a proper PS/2 driver and return ASCII values from typed keys, so that I can build that simple boot shell.

View all 11 project logs

  • 1
    Make sure you have GNU make and binutils installed.

    I'm using GNU Make 4.0, but I don't think I'm using any 4.0-specific features.  You can probably get by with an older version if that's all that's available to you.  I know that 3.81 should absolutely work.

    I only use Make; however, if you need to install the RISC-V GCC from sources, you'll also need the binutils.  GCC's installation instructions should help you if you need them.

  • 2
    On your FPGA workstation, install RISC-V compatible, 64-bit version of GCC and binutils.

    I'm using GCC 5.6.0 personally; I believe the latest is 7.0.

  • 3
    On your FPGA workstation, install Xilinx Webpack ISE 14.7.

    You may also use any compatible version of Webpack ISE that works for you.  I selected this version because it was the latest which supported the Spartan XC3S1200E chip on my board.  (DISCLAIMER: I remember having to fight a battle with ISE to get it properly installed, due to a missing library.  Alas, I no longer remember details.)

View all 12 instructions

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates