The RG5 processor is a 16-bit soft core processor, designed more to be small than quick. It's one of my (pdt) current play-time projects.
There are plenty of soft core processors around, e.g. see https://en.wikipedia.org/wiki/Soft_microprocessor and opencores.org. GitHub also has a few, see https://github.com/cliffordwolf/picorv32 for a size optimized RISCV. FPGA vendors also provide PicoBlaze, MicroBlaze, Nios II, Mico8 and Mico32.
I am also working on an assembler/simulator for the RG5 processor. It's written in Tcl. Assembler source files look a lot like assembler, but are Tcl scripts.
For the sake of size:
The RG5 processor doesn't have any general purpose registers. Its only registers are PC (program counter), SR (status register - Z N C V I flags), SP (stack pointer), FP and GP (base pointer registers).
Instead of having general purpose registers, the processor directly uses global or stack-frame variables in FPGA block RAM.
So lets say variable 'a' is on a stack frame:
The above instructions all assemble to either one, two, or three 16-bit instructions words.
The processor also supports structure operations, which assemble to two or three instruction words:
But I've yet to fully implement those in my assembler ...
With the above in mind, the clock example assembler code below should be reasonably clear to understand. The clock displays time on 7-segment displays, and has three buttons, mode, up and down, for changing time.
memmap data -start 0x0000 -end 0x01ef memmap rodata -start 0x01f0 -end 0x01ff memmap code -start 0x0200 -end 0x03ff # i/o registers byte statReg -io 0xffc0 byte ctrlReg -io 0xffc1 byte lsMinutesReg -io 0xffc2 byte msMinutesReg -io 0xffc3 byte lsHoursReg -io 0xffc4 byte msHoursReg -io 0xffc5 # status register bits constant byte modeBtn 0x01 constant byte upBtn 0x02 constant byte dnBtn 0x04 constant byte timed 0x80 # operating mode byte mode # time byte fractions byte seconds byte minutes byte hours # for reading buttons byte btns byte oldBtns # scratchpad variable byte b # 7-segment display codes constant byte displayCodes -rodata 0x3f 0x06 0x5b 0x4f 0x66 0x6d 0x7d 0x07 \ 0x7f 0x6f # ------------------------------------------------------------------------------ section main { offsets statReg ctrlReg lsMinutesReg msMinutesReg lsHoursReg msHoursReg \ mode fractions seconds minutes hours btns oldBtns b %gp 0 # initialisation mv [memmap data -end] %sp foreach var {ctrlReg mode fractions seconds minutes hours oldBtns} { clr $var } # wait for timer to trigger @ l_waitTimer test timed statReg bz l_waitTimer # check for one button pressed mv statReg btns foreach val {modeBtn upBtn dnBtn} { cmp $val btns beq l_btns } mv btns oldBtns cmp 0 mode beq l_updateTime br l_updateDisplay1 # update buttons status @ l_btns mv btns b xor oldBtns btns and b btns mv b oldBtns # check for mode button press test modeBtn btns bz l_mode2 inc mode cmp 3 mode bne l_mode1 clr mode @ l_mode1 mv mode ctrlReg # check for display mode @ l_mode2 cmp 0 mode beq l_updateTime # check for update minutes mode cmp 1 mode bne l_hours1 # minutes up test upBtn btns bz l_mins inc minutes cmp 60 minutes bne l_updateDisplay1 clr minutes br l_updateDisplay1 # minutes dn @ l_mins test dnBtn btns bz l_updateDisplay1 dec minutes bnn l_updateDisplay1 mv 59 minutes br l_updateDisplay1 # hours up @ l_hours1 test upBtn btns bz l_hours2 inc hours cmp 24 hours bne l_updateDisplay1 clr hours br l_updateDisplay1 # hours dn @ l_hours2 test dnBtn btns bz l_updateDisplay1 dec hours bnn l_updateDisplay1 mv 23 hours br l_updateDisplay1 # update time @ l_updateTime foreach var {fractions seconds minutes hours} val {20 60 60 24} { inc $var cmp $val $var bne l_updateDisplay1 clr $var } # update display @ l_updateDisplay1 callf getDisplayCodes minutes &lsMinutesReg &msMinutesReg callf getDisplayCodes hours &lsHoursReg &b cmp 10 hours buge l_updateDisplay2 clr b @ l_updateDisplay2 mv b msHoursReg br l_waitTimer } # convert num to two 7-segment display codes void function getDisplayCodes {byte num word p_lsDigit word p_msDigit} { word tens word p_dispCode clr tens @ l1 cmp 10 num bult l2 sub 10 num inc tens br l1 @ l2 mv &displayCodes p_dispCode add.w num p_dispCode mv.b *p_dispCode *p_lsDigit mv &displayCodes p_dispCode add tens p_dispCode mv.b *p_dispCode *p_msDigit retf }
And below is an additional bit of code that creates the Tk GUI below, so that the clock assembler code can be simulated and tested.
sim -init { set timer 0 set modeBtn 0 set upBtn 0 set dnBtn 0 set segments [list] set digit {{1 0 9 0} {10 1 10 9} {10 11 10 19} {1 20 9 20} {0 11 0 19} {0 1 0 9} {1 10 9 10}} set minsColour red set hrsColour red # read status callback function proc readStatCb {addr val} { global timer modeBtn upBtn dnBtn set rv [expr {$modeBtn | $upBtn << 1 | $dnBtn << 2}] incr timer if {$timer == 200} { set rv [expr {$rv | 0x80}] set timer 0 } return $rv } # write display callback function proc writeDispCb {addr val} { global minsColour hrsColour if {$addr == [addr ctrlReg]} { set minsColour red set hrsColour red if {$val == 1} { set hrsColour darkred } if {$val == 2} { set minsColour darkred } } setDigit 3 [var lsMinutesReg] $minsColour setDigit 2 [var msMinutesReg] $minsColour setDigit 1 [var lsHoursReg] $hrsColour setDigit 0 [var msHoursReg] $hrsColour return $val } # offset position by x and y proc offsetPos {pos x y} { lassign $pos x1 y1 x2 y2 incr x1 $x incr y1 $y incr x2 $x incr y2 $y return [list $x1 $y1 $x2 $y2] } # set digit number (0 to 3) to hexCode proc setDigit {num hexCode colour} { set i [expr {$num * 7}] for {set j 0} {$j < 7} {incr j} { set c [expr {$hexCode & 0x1 ? $colour : "gray"}] .disp itemconfigure [lindex $::segments $i] -fill $c set hexCode [expr {$hexCode >> 1}] incr i } } # build gui package require Tk wm title . Clock wm protocol . WM_DELETE_WINDOW exit ttk::checkbutton .modeBtn -text Mode -variable modeBtn ttk::checkbutton .upBtn -text Up -variable upBtn ttk::checkbutton .dnBtn -text Down -variable dnBtn tk::canvas .disp -width 200 -height 40 grid .modeBtn .upBtn .dnBtn grid .disp - - foreach xpos {20 35 55 70} { foreach l $digit { lappend segments [.disp create line [offsetPos $l $xpos 10] \ -width 2 -fill gray] } } .disp create line 49 30 51 30 -width 2 -fill red # callback functions for i/o registers readCb statReg readStatCb writeCb ctrlReg writeDispCb writeCb msHoursReg writeDispCb startSim vwait forever
For more details, see http://www.p-code.org/clock_example.html
Once the assembler code has been simulated, it can be used with a reasonably high level of confidence that it will work first time in hardware. Indeed that is what happened. The image below shows a breadboard lashup using a rather nice old style bubble 7-segment display, connected to an Altera FPGA dev board.
Jorge - 2016-10-14 22:02:56
do you have plans to share the actual Tcl assembler/simulator?
pdt Yes, I'll make the source available, once it's in a more complete state.
chw shortly after: would you Jorge please contact me by private email?
Jorge says: could not find your email address, but mine is on my personal page on this wiki...