Repository navigation
Home
Years ago, a co-worker of mine at Be posted an interesting bit of sample code called Whack. The original article is here:
And source code is here:
The app allowed the user to enter a formula that would compute the color value per pixel to display patterns on the screen. I've attempted to re-create the application on FPGA.
The compiler reads a C expression and outputs instructions with the following format:
+---------+---------+---------+----------+------+-------------+ | dest(3) | srca(3) | srcb(3) | op (4) | c(1) | imm (32) | +---------+---------+---------+----------+------+-------------+
-
op Which arithmetic operation to perform, one of:
opcode operation 0 or 1 xor 2 and 3 equal 4 not equal 5 greater 6 less than 7 greater or equal 8 less than or equal 9 shift left 10 shift right 11 add 12 subtract 13 multiply -
dest Destination register to store result
-
srca first source operand register
-
srcb second source operand register
-
c If true, the second operand will use the constant value instead of a register value
-
imm If c is set, use this constant value as the second operand.
An instruction that is all zeroes is no operation (it corresponds to 'and r0, r0, r0', which has no effect).
A pixel processor has 8 registers:
| index | usage |
|---|---|
| 0 | general purpose scratchpad |
| 1 | general purpose scratchpad |
| 2 | general purpose scratchpad |
| 3 | general purpose scratchpad |
| 4 | current x position of pixel (read only) |
| 5 | current y position of pixel (read only) |
| 6 | frame number (read only) |
| 7 | color value (write only) |
The hardware latches values written to r7 and displays them on the screen when the program is finished. The processor does support memory accesses or control flow.
The compiler accepts C style expressions. The result of the expression is treated as a 24 bit packed RGB color value.
This system does not have a framebuffer and computes the pixel values on the fly. The FPGA runs at 50 Mhz, but the output VGA clock is 25 Mhz, which means it must generate a pixel every other cycle. Two cycles aren't enough to do anything interesting. The solution is to process eight pixels in parallel. When it finishes computing colors for a batch of pixels, it copies them in parallel into a FIFO, and starts processing the next batch of pixels. The FIFO feeds a pixel to the VGA R2R DAC every other cycle. This means it will execute 16 instructions per pixel. The pixel processor executes 16 instructions from program memory in a repeating loop and doesn't have a notion of program length; the compiler pads the program out with NOPs.
Input Expression:
(((x >> 6) & 1) * 0xff) | (((x >> 7) & 1) * 0xff00) | (((x >> 8) & 1) * 0xff0000)Compiled Code:
shr r0, r4, #6
and r0, r0, #1
mul r0, r0, #255
shr r1, r4, #7
and r1, r1, #1
mul r1, r1, #65280
or r0, r0, r1
shr r1, r4, #8
and r1, r1, #1
mul r1, r1, #16711680
or r0, r0, r1
and r7, r0, r0Image:
Input Expression:
(((x >> 4) & 1) ^ ((y >> 4) & 1)) * 0xffffffCompiled Code:
shr r0, r4, #4
and r0, r0, #1
shr r1, r5, #4
and r1, r1, #1
xor r0, r0, r1
mul r0, r0, #16777215
and r7, r0, r0Image:

Input Expression:
(((x & 0x1f) == 0) | ((y & 0x1f) == 0)) * 0xffffffCompiled Code:
and r0, r4, #31
eq r0, r0, #0
and r1, r5, #31
eq r1, r1, #0
or r0, r0, r1
mul r0, r0, #16777215
and r7, r0, r0Image:
Input Expression:
((x - y) > 0) * 0xffffffCompiled Code:
sub r0, r4, r5
gt r0, r0, #0
mul r0, r0, #16777215
and r7, r0, r0Image:
Input Expression:
(((x+y)^x)+f)Compiled Code:
add r0, r4, r5
xor r0, r0, r4
add r0, r0, r6
and r7, r0, r0Image:
http://www.youtube.com/watch?v=1tVbqoH7bzc&feature=youtu.be