Last visit was: Mon Mar 01, 2021 9:46 am
It is currently Mon Mar 01, 2021 9:46 am

 [ 8 posts ] 
 Enhanced Tiger Single Board Computer 
Author Message

Joined: Mon Jan 22, 2018 2:49 pm
Posts: 14
Welcome to the user-defined TigerSBC DDE (Disintegrated Design Environment).
TigerSBC is fun to program, and if you like being in the driver's seat, this machine might be for you. is a snapshot of experimentally verified hardware and software source files, for free use at one's own risk.

Many Python3 and Verilog source iterations led to this snapshot. Mistakes were made along the way, and corrected. (See doc/koans.txt.)
It is hoped that users will enjoy making and correcting their own mistakes, and sharing their successes.

This DDE implements a 12-bit 36 MHz lab/maker computer on a Lattice iCE40-HX8K FPGA Breakout Board, and its assembler.

Why 12 bits versus industry norms?
1) more powerful than 8 bits
2) simpler and faster than 16/32/64 bits
3) object code is compact, minimizing memory overhead
4) naturally matches popular 12-bit SPI ADCs and DACs for the lab/maker computer mission

TigerSBC's performance and code-density apparently rivals commodity MCUs.
The entire 4Kx12 code/data/io memory space is on-chip.
All reads and writes complete in one (1) clock cycle.

5V0 power and the host interface are provided via USB.
Memory-mapped io includes a 28-bit signed multiplier/accumulator and a 48-bit timer.
Efficient 12-bit bit-banging parallel io, and an 8-bit mode 0 master SPI interface are also provided for 3V3 user io.
doc/EB85_pins.txt summarizes user io on the J2 and J4 2x20 0.1 inch (2.54 mm) pitch connectors.

Software libs and apps include Fast Fourier Transform and convolution DSP amenities.
A 512-point complex FFT takes ~13 ms.
An 8-point complex FFT takes ~78 us.

Tigerasm is written in Python3, providing a powerful syntax and interactive user interface.
New users should first learn how to program their own Tigerasm libs and apps for the existing hardware implementation.
Experienced users who speak Verilog might mutate the hardware as they see fit.

The Tiger CPU executes all (16) instructions in one (1) clock cycle. See Verilog/Tiger_opcodes.v for the summary.
The data and program counter stacks are register arrays with RPN arguments and results (smells like, but is not Forth).
Unique instruction fetch logic frequently saves clock cycles. See doc/instruction_fetch.txt for the commentary.

Tigerasm macros and procedures provide additional (multi-clock) operators.
Flagship examples are in lib/ and apps/
A simple example is in apps/

A minimal DDE to use TigerSBC as-is requires:
* a Lattice iCE40-HX8K Breakout Board and included USB cable, and a reset switch (see doc/reset.txt)
* a text editor (e.g. Notepad++)
* Python3 for the Tiger assembler
* a serial terminal emulator with raw file send capability for communication via USB/UART at 115200,8,n,1 (e.g. Tera Term)
* Lattice Semiconductors Diamond to burn the existing FPGA configuration (pre-generated by iCEcube2 on the author's host)

A full DDE to create your own hardware mutations also requires:
* Lattice Semiconductors iCEcube2 for FPGA design
* Icarus Verilog for new module design and simulation

Getting started (Win10 example, Linux translations are straightforward):
0) Unzip to your desktop and study doc/overview.html (this file).
1) Obtain the requisite hardware and install the requisite software (punt to vendor doc).
2) Rename asm.cmd.txt to asm.cmd.
3) Rename sim/ivsim.cmd.txt to sim/ivsim.cmd.
4) Connect your board (with closed reset switch connected on J2) via USB. Be curious about which COMx the OS decided to assign.
5) Launch Diamond to burn your board as a TigerSBC with the USB/UART app loader:
select Create a new blank project, click OK
under the Device Family heading, select iCE40
under the Device heading, select iCE40HX8K
under the Operation heading
select Access mode: SPI Flash Programming
select Programming file: FPGAburn/TigerSBC_bitmap.bin
click OK
click the Program icon
if failure, try hot-switching to another USB physical connection and retry (this happens if you have more than 1 board)
on success, quit Diamond without saving the configuration
6) Launch a command prompt and enter these lines to test the assembler:
cd Desktop/TigerSBC
asm apps/
(optional: investigate the suggested Python3 amenities)
7) Launch the serial terminal emulator, configured 115200,8,n,1 on the currently-assigned COM port:
close the reset switch
open the reset switch, you should see
Tiger is ready to load...
raw file send apps/
you should now see:
Tiger is ready to load...done
Hello, world!
the red led D7 on the board should be blinking

For hints on writing your own apps, see FPGAburn/, lib/*.py, apps/*.py, and defines a 2-pass assembler that reads in source scripts via the include('filename') core procedure.
In the first pass, new symbols and (explicit) forward references are collected.
In the second pass, the final object code image is generated, ready to be saved to disk as an app (or a hex) file.

Although based on Python3, indentation in Tigerasm source is purely cosmetic.
The examples in this snapshot reflect the author's preferred style.

All Tigerasm symbols are global and must be unique. The order of encounter in source is important.
If an error occurs, inspect _includes_ to see the list of included source files.
The last file in _includes_ is where the error was detected, but not necessarily the root cause.
Python3 dir() and globals() list the known symbols for further inspection.
Tigerasm dump_ram(start_adr, end_adr) shows raw hexadecimal object code and data.

Typical Tigerasm libs and apps inherit the following host interface procedures from FPGAburn/
lbl('rx') # -> d
# await zero-extended 8 lsbs from USB/UART

lbl('tx') # d ->
# transmit 8 lsbs of d to USB/UART

lbl('tx_ascii') # adr ->
# transmit a zero-terminated ascii string to USB/UART

Those interested in Verilog will find hints in Verilog/*.v.
Edit ram.v to correct the absolute reference to FPGAburn/ram.hex (the USB/UART app loader initial memory image).
iCEcube2 might then be used to build a new TigerSBC project for the board.
(Arcana: this snapshot required using the <right-click>Synthesis Tool: Synplify Pro option.)
Hardware mutations require followup changes to Verilog/TigerSBC.pcf and the io addresses defined in

To try the Icarus Verilog simulator, launch a command prompt and enter these lines:
cd Desktop/TigerSBC/sim
ivsim spi
The simulation display was also saved to spi.log for posterity, try:
copy spi.log con

For further consideration:
This single-core implementation left most of the iCE40-HX8K FPGA resources available.
Future mutations might explore parallel execution of multiple cpu+ram cores and their io interfaces.

Have fun being in the driver's seat!

You do not have the required permissions to view the files attached to this post.

Last edited by Myron Plichota on Wed Sep 09, 2020 1:12 pm, edited 1 time in total.

Wed Sep 09, 2020 11:20 am

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1579
Looks interesting! 12 bit computing on Lattice FPGA, with python assembler... I have a MyStorm BlackICE board, which is generally used with the open source icestorm toolchain - if that combination works, it might be interesting to post on the mystorm forums.

Wed Sep 09, 2020 11:41 am

Joined: Mon Jan 22, 2018 2:49 pm
Posts: 14
I'd be delighted to hear about other users' successes implementing TigerSBC on other hardware and toolchains!

Wed Sep 09, 2020 11:58 am

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1579
I had a quick go with the icestorm/arachne/yosys toolchain, and it seems to have worked...

$ yosys -q -p "synth_ice40 -top TigerSBC -abc2 -blif TigerSBC.blif" *.v */*.v
Warning: Replacing memory \d with list of registers. See TigerCPU.v:138
Warning: Replacing memory \p with list of registers. See TigerCPU.v:89

(That is, the 16 data stack and 8 program counter stack registers are registers, which seems reasonable)

$ arachne-pnr -d 8k -p TigerSBC.pcf TigerSBC.blif -o TigerSBC.asc

After packing:
IOs          46 / 206
GBs          0 / 8
  GB_IOs     0 / 8
LCs          1659 / 7680
  DFF        465
  CARRY      68
  DFF PASS   125
BRAMs        12 / 32
WARMBOOTs    0 / 1
PLLs         1 / 2
After placement:
PIOs       42 / 206
PLBs       368 / 960
BRAMs      12 / 32

$ icepack TigerSBC.asc TigerSBC.bin
$ ls -lh TigerSBC.bin
-rw-rw-r-- 1 ed ed 132K Sep  9 21:54 TigerSBC.bin

$ icetime -d hx8k TigerSBC.asc
// Reading input .asc file..
// Reading 8k chipdb file..
// Creating timing netlist..
// Timing estimate: 26.04 ns (38.40 MHz)

Wed Sep 09, 2020 8:56 pm

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1579
Just had a quick try with the nextpnr router, which is the successor to arachne, and the implementation came out a bit quicker:

$ yosys -q -p "synth_ice40 -top TigerSBC -abc2 -json TigerSBC.json" *.v */*.v
$ nextpnr-ice40 --hx8k --package ct256  --pcf TigerSBC.pcf --json TigerSBC.json --asc TigerSBC.asc
$ icepack TigerSBC.asc TigerSBC.bin
$ icetime -d hx8k TigerSBC.asc
// Reading input .asc file..
// Reading 8k chipdb file..
// Creating timing netlist..
// Timing estimate: 23.25 ns (43.01 MHz)

Wed Sep 09, 2020 9:32 pm

Joined: Mon Jan 22, 2018 2:49 pm
Posts: 14
Go man! Go!

Wed Sep 09, 2020 10:56 pm

Joined: Mon Jan 22, 2018 2:49 pm
Posts: 14
If you have technical data on the MyStorm BlackICE board, we might discuss the filthy details of FPGA pin mapping :)

Never mind, I located some IceCore info, but damned if I could find the schematic PDF!

Wed Sep 09, 2020 11:54 pm

Joined: Mon Jan 22, 2018 2:49 pm
Posts: 14
Here's a spectrum analyzer app based on the release libraries.
include('FPGAburn/') # must be on the first line

# text mode spectrum analyzer
# 64-point FFT
# harmonics 1..31 are shown as real (red) and imaginary (blue) horizontal bargraph pairs
# terminal should be at least 80x64


def12('passes', 6) # 3..9
def12('points', 1 << passes) # 8..512


defnum('h1', 60) # fft first harmonic Hz
defnum('fs', h1 * points) # adc sampling rate
defnum('ticks', fclk / fs) # clock ticks per sample
def12('kt11', int(ticks) & ALLBITS)
def12('kt23', int(ticks) >> 12)

lbl('wakeup'); data([0,0]) # next tl11,tl23 trigger

lbl('resync') # ->
# set wakeup (after inattention)
  lld(tl11); lit(kt11); adx(); lld(tl23); add(); lit(kt23); add()
  lst(wakeup+1); lst(wakeup); ret()

lbl('sync_x'); data([0])
lbl('sync') # ->
# await next wakeup, reschedule
    lld(wakeup); lld(tl11); neg(); adx(); lld(wakeup+1); add(); lld(tl23); inv(); add()
    lband(SIGNBIT); lst(sync_x); drop(); lld(sync_x); ljz(sync)
  lld(wakeup); lit(kt11); adx(); lld(wakeup+1); add(); lit(kt23); add()
  lst(wakeup+1); lst(wakeup); ret()

lbl('samples'); data([0 for n in range(points)])

lbl('capture_i'); data([0])
lbl('capture') # ->
# fill the sample buffer at nominal fs from adc1 channel 3
  lit(0); lst(capture_i)
    lld(capture_i); lbxor(points); ljz(fwd('capture_done'))
      lit(3); lit(spi_adc1); lcall(mcp3208_acquire); lbxor(SIGNBIT); lld(capture_i); last(samples)
      lld(capture_i); inc(); lst(capture_i)

lbl('real_in_adr'); data ([0])
lbl('real_in_i'); data ([0])
lbl('real_in') # adr ->
# copy array at adr to real in bit-reversed order, imag is zeroed
  lst(real_in_adr); lit(0); lst(real_in_i) # ->
    lld(real_in_i); lbxor(points); ljz(fwd('real_in_done'))
      lld(real_in_adr); lld(real_in_i); ald() # -> d
      lld(real_in_i); lald(brev); last(real) # ->
      lit(0); lld(real_in_i); last(imag)
      lld(real_in_i); inc(); lst(real_in_i)

lbl('abs') # +-d -> +d
# absolute value of anything but SIGNBIT (the most-negative value)
  tt(); lband(SIGNBIT); ljz(fwd('abs_pos'))
    neg() # singularity: abs(SIGNBIT) == SIGNBIT, which is the wrong sign!

lbl('glyph'); data([ord('0')]) # '-'|'0'|'+'

lbl('pick_glyph') # d -> d
# determine hbar* plot character
  tt(); ljz(fwd('pick_glyph_zero'))
    tt(); lband(SIGNBIT); ljz(fwd('pick_glyph_pos'))
      lit(ord('-')); lst(glyph); ret()
  lit(ord('+')); lst(glyph); ret()
  lit(ord('0')); lst(glyph); ret()

lbl('hbar128') # d ->
# print a horizontal bar of up to 128 glyphs
# misbehaves if d == SIGNBIT because of the absolute value singularity
  lcall(pick_glyph); lcall(abs); asr(); asr(); asr(); asr(); inv() # -> lc # loop control
  lit(ord('\r')); lcall(tx); lit(ord('\n')); lcall(tx) # newline
    tt(); ljz(fwd('hbar128_done'))
      lld(glyph); lcall(tx)
      inc() # -> lc'
  drop(); ret() # ->

lbl('ansi_clear'); ascii('\x1b[2J\x1b[H')
lbl('ansi_red'); ascii('\x1b[31m')
lbl('ansi_blue'); ascii('\x1b[34m')
lbl('ansi_default'); ascii('\x1b[0m')

lbl('plot_spectrum') # ->
# plot real (red) and imaginary (blue) horizontal bars for each unexceptional fft harmonic
  lit(ansi_clear); lcall(tx_ascii) # clear screen, home cursor top left
  lit(1) # -> harmonic
    tt(); lbxor(points >> 1); ljz(fwd('plot_spectrum_done'))
      lit(ansi_red); lcall(tx_ascii); tt(); lald(real); lcall(hbar128)
      lit(ansi_blue); lcall(tx_ascii); tt(); lald(imag); lcall(hbar128)
      inc() # -> harmonic'
  drop(); lit(ansi_default); ljmp(tx_ascii) # ->

    lcall(rx); drop() # await a keystroke
#    lit(cos); lcall(real_in);
#    lit(sin); lcall(real_in);
    lcall(capture); lit(samples); lcall(real_in);
    lcall(fft); lcall(plot_spectrum)

lbl('app_end') # must be on the last line

You do not have the required permissions to view the files attached to this post.

Fri Sep 25, 2020 2:35 am
 [ 8 posts ] 

Who is online

Users browsing this forum: CCBot and 0 guests

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
Powered by phpBB® Forum Software © phpBB Group
Designed by ST Software