Skip to content

UNIXv7 ported to RISC-V, specifically the Longnan Nano SBC

Notifications You must be signed in to change notification settings

robertlipe/riscv7

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

This is the (not-yet working) port of UNIXv7 system to RISC-V.

It's currently targeting the GD32V as found on the Longnan Nano, a
super-inexpensive ($5-ish) SBC that includes a full USB-C controller,
including OTG. This makes it possible to appear as a mass storage device,
a serial port, or other USB peripherals. This is more powerful than
devices like the K210 or BL602 which require an external USB->Serial
adapter that can support none of these. RISCv7 does not (yet) use any of
this awesomeness.

There are some remnanats in the code of me starting with the K210. That
is a 64-bit part that has 8MB of RAM and 2 cores. While I may go back to
that part some day (more RAM and protected mode would be nice) I learned
early on that there were a lot of structures with implicit sizes that
burst into flames with int > 32 bits. In the name of focus, I went down
to a single part.

An early design choice was to NOT design my own ABI and calling convention.
Nor did I choose to torture myself with ancient Make. Relying on a sane
GNU make on the host is fair game. It's easy to find riscv64-gcc in
prepackaged binaries (Mac: Homebrew. Debian has a package) where 32-bit
tools are harder to find. The tools support 32-bit modes so the convenience
of calling riscv32-gcc over riscv64-gcc with -march=rv32imac -mabi=ilp32 to
get 32-bit code isn't worth it[1]. The ABI is "whatever these tools do". Like
the early UNIXes, binary compatibilty isn't a concern; I may change system
call numbers or visible symbols pretty much at whim.

Similarly, I've not fought a fierce battle to keep the code K&R. As this
code is meant to be educational (it certainly has been for this author)
more than historical purity, familiarity with ANSI function declarations,
judicious use of declarations, va_arg, not relying on implicit int, the
presence of 'void', the absence of 'register' and so on are all considered
good taste. Indeed, especially with 64-bit being in sight, implicit int
and such can lead to really screwy stack frames so we're just trying to
head that off.

I started development thinking I would write ALL the code myself and not
rely on any namby-pamby vendor code. That quickly got old. In sys/n200
(another unfortunate naming choice) is code for the Nucleisys 200 core as
used in the GD32V, the Gigadevice libraries for interfacing with SPI, DMA,
and more, as well as some other third party libraries. There is evidence
in this directory of several failed experiments with different abstractions.


I've been happily using the SEGGER Mini EDU JTAG device. Using something
like Openocd with an FTDI board is probably possible, but that's what I
started with.

The best way to build the kernel is to be in usr/sys/conf and run make.
There is a convenience script named 'upload' which does the obvious thing,
calling Jlink with the appropriate fiddly parameters. 'run_nano' does a
build, uploads the kernel to the board, and starts a GDB server.

For debugging, there are x/ and z/ subdirectories (bad, I know...) that
have .gdbinits that offer convenience macros to reboot the kernel, reload
it (via GDB), start the remote debugging, and automatically have breakpoints
on panic and other places you hope to not be. These will probably need
customized to a developer's system and style.

Little attention has been given to user-space so far. I expect most of it
will fall into place. crt0.S for these parts is straight-forward. I just
need to work through things like the global for errno and the assembly
code that sets it, the header tension between usr/include and usr/sys/h,
and signal handlers that need to decode interrupted instructions so that
the $PC can be set right. There is also the need for standardized kernel
exception frames.



Status & Known problems:

Boot is working.

Timer interrupts are working.

The display is mostly working. It's too small to be really useful, but
displaying interrupt ticks or enter/exit style prints can still be handy.
The display is NOT hooked as a console device with ANSI escape sequences
nor is it even hooked as a serial console. It's shoved into putchar()
inside sc.c,.


Memory management is a mess.  The code I started from, Robert Nordier's
386 port of UNIXv7 has some hard coded page numbers in memory layout
in the kernel. This has proven hard to undo.

The code currently gets to the point where it trampolines the assembler
code to start an exec() to load and run.

Sometime the LCD doesn't wake properly. I think there's something missing
in the reset sequence. As embarrassing/ridiculous as this sounds, building
and running gd32vf103inator/examples/LonganNano will "fix" the display.
Once it's run and whacked the LCD (DMA? SPI?) once, the LCD will literally
run for days and across hundreds of load/run cycles.

The chip has a hardware scroll, but I cannot make it scroll in the axis it
needs to scroll. Horizontal scroll is not very useful in a UNIX console.
One possible way to implement scrolling is to define a text frame buffer
(maybe with 8-bit color like CGA or VGA) and fully redraw the screen on
a scroll. I'm not sure if that's a good use of our memory budget.

SD card support is working at a proof-of-concept level. I can open
a DOS-formattted memory card and read files and directories from it.
I've not implemented partition table support. With SD cards being so
cheap, I'm not sure it makes sense. More importantly, I've not implemented
any "real" UNIX-level disk support. Even being a serial device attached
to SPI, SD is so crazy fast relative to the VAX disk that I'm not sure
that just spin-waiting a DMA-driven interface isn't the way to go. Shimming
the code in tf_card.c for disk_read() and disk_write() for synchronous
reads and writes may be OK.

There is no root filesystem created by the build process. The kernel is
just injected into flash memory and executed. It works at a reasonable
speed because flash is copied to SRAM on boot. SRAM is apparently cheaper
than caches at this size. (!!)

Segger's equivalent of ARM's Semihosting would probably be good
to implement for debugging.

[1] For more info on RISC-V embedded chains, see 
  https://riscv.org/wp-content/uploads/2015/02/riscv-software-stack-tutorial-hpca2015.pdf
  https://github.com/riscv/riscv-gnu-toolchain

About

UNIXv7 ported to RISC-V, specifically the Longnan Nano SBC

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published