640x480x24bit display for DE2-115 board

Here's a program that runs on the DE2-115 board and uses the SDRAM driver to buffer a 24-bit display. Because the FPGA only goes 60MHz, it needs all the SDRAM bandwidth to feed the display, so a cog writes the buffer, then leaves it alone for display. The last section of code is the one that plots the pixels into the SDRAM. It uses QARCTAN to determine what color the pixel should be, based on its location:
I'd put a picture of it up, but my camera's batteries are dead.
'
' SDRAM Driver and 640x480 24bbp VGA Demo
'
DAT org
reps #4,#1
setptra sdram_par 'write initial command list
wrlong h00000000,ptra++ 'skip_done then sdram_address (x2)
wrlong h00000008,ptra++ 'terminate with end_of_list
setcog #8
coginit sdram_adr,sdram_par 'launch SDRAM driver into cog1
coginit graph_adr,graph_par 'launch graph driver into cog2
jmp #vga
sdram_adr long driver<<2 + $E80
sdram_par long $E80
graph_adr long driver<<2 + $E80 + $D0
graph_par long $E80
h00000000 long $00000000
h00000008 long $00000008
'*******************************
'* VGA 640 x 480 x 8:8:8 RGB *
'*******************************
' P4 = VSYNC
' P3 = RED
' P2 = GREEN
' P1 = BLUE
' P0 = HSYNC
CON
s = 44 'scales DAC output (s = 0..128)
DAT
vga setptra sdram_par 'set ptra
setctra #%00111 'set ctra for PLL*8
setfrqa frqa_ 'set frqa
setvid #%0101 'set vid for vga output, negative hsync
setvidy vidy_ 'set yiq coefficients
setvidi vidi_
setvidq vidq_
cfgdacs #%11_11_11_11 'set dacs to video mode
setquad #0 'address QUADs at 0..3
'
'
' Field loop
'
field mov pixel_ptr,#0 'reset SDRAM pixel pointer
call #read128 'issue SDRAM read command to prime buffer
mov x,#33 'top blanks
call #blank
mov x,lines_vis 'set visible lines
line call #sync 'do horizontal sync
mov y,#5 'display 640 pixels
:pix4 call #read128 'issue SDRAM read command to begin loading other buffer
reps #128/4,#8 'read 32 quads and push them into stack RAM
waitvid mode,#0 'issue VID command
rdquad ptrb++ 'read a quad and push it
nop
nop
nop
pusha 0
pusha 1
pusha 2
pusha 3
notb mode,#31 'toggle stack RAM offset in VID command
djnz y,#:pix4 'loop until 5 sets of 128 pixels (640 total)
djnz x,#line 'another line?
mov x,#10 'bottom blanks
call #blank
clrp #0 'do vertical sync
mov x,#2
call #blank
setp #0
jmp #field 'field loop
blank call #sync 'blank lines
waitvid v640,sync_color0
djnz x,#blank
blank_ret ret
sync 'horizontal sync
sync_ret retd
waitvid v16,sync_color0
waitvid v96,sync_color1
waitvid v48,sync_color0
'
'
' Issue SDRAM read command for 128 pixels (1024 bytes)
'
read128 rdlong t,ptra[0] 'wait for last SDRAM command done
tjnz t,#read128
setptrb sdram_cmd 'get current data ptr into ptrb
xor sdram_cmd,h200 'update command data ptr
read128_ret retd 'delayed return
wrlong pixel_ptr,ptra[1] 'issue SDRAM address
add pixel_ptr,h200 'update pixel pointer
wrlong sdram_cmd,ptra[0] 'issue SDRAM read command
pixel_ptr long 0
sdram_cmd long $1006 '128 pixels, toggle between $1006 and $1206
h200 long $200
'
'
' Initialized data
'
frqa_ long $0D6D_3A06 '25.175MHz
vidy_ long $00_01_00_00 * s 'red gain
vidi_ long $00_00_01_00 * s 'green gain
vidq_ long $00_00_00_01 * s 'blue gain
' color base mode clocks/pixel clocks/waitvid
mode long $00 << 24 + $F << 20 + 1 << 13 + 128
lines_vis long 480
sync_color0 long $0_000000
sync_color1 long $1_000000
v16 long 16
v96 long 96
v48 long 48
v640 long 640
t long 0
x long 0
y long 0
driver 'SDRAM driver follows
'********************************
'* *
'* Propeller II SDRAM Driver *
'* for 16M x 16 devices *
'* (FPGA version) *
'* *
'* Version 0.1 *
'* 9 April 2013 *
'* by Chip Gracey *
'* *
'********************************
{
SDRAM connections:
P85 = cke (held high) Port C
P84 = cs
P83 = ras
P82 = cas
P81 = we
P80 = udqm (not used)
P79 = ldqm (not used)
P78..P77 = ba[1..0]
P76..P64 = a[12..0]
P63..P48 = dq[15..0] Port B
Note: All pin directions have a 2-clock delay
All pin outputs going to SDRAM have a 3-clock delay, since they are registered at the pin
All pin inputs coming from SDRAM have a 2-clock delay, since they are registered at the pin
Commands (pairs of longs):
name quads bytes hub_address (+0, set 2nd) sdram_address (+1, set 1st)
------------------------------------------------------------------------------------------------------------------------
rw_1024 64 1024 %xxxx_xxxx_xxxx_xxxA_AAAA_AAAA_AAAA_W111 %xxxx_xxxA_AAAA_AAAA_AAAA_AA00_0000_0000
rw_512 32 512 %xxxx_xxxx_xxxx_xxxA_AAAA_AAAA_AAAA_W110 %xxxx_xxxA_AAAA_AAAA_AAAA_AAA0_0000_0000
rw_256 16 256 %xxxx_xxxx_xxxx_xxxA_AAAA_AAAA_AAAA_W101 %xxxx_xxxA_AAAA_AAAA_AAAA_AAAA_0000_0000
rw_128 8 128 %xxxx_xxxx_xxxx_xxxA_AAAA_AAAA_AAAA_W100 %xxxx_xxxA_AAAA_AAAA_AAAA_AAAA_A000_0000
rw_64 4 64 %xxxx_xxxx_xxxx_xxxA_AAAA_AAAA_AAAA_W011 %xxxx_xxxA_AAAA_AAAA_AAAA_AAAA_AA00_0000
rw_32 2 32 %xxxx_xxxx_xxxx_xxxA_AAAA_AAAA_AAAA_W010 %xxxx_xxxA_AAAA_AAAA_AAAA_AAAA_AAA0_0000
rw_16 1 16 %xxxx_xxxx_xxxx_xxxA_AAAA_AAAA_AAAA_W001 %xxxx_xxxA_AAAA_AAAA_AAAA_AAAA_AAAA_0000
skip_done 0 0 %0000_0000_0000_0000_0000_0000_0000_0000 %xxxx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx
end_of_list 0 0 %0000_0000_0000_0000_0000_0000_0000_1000 %xxxx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx
The driver scans a list for commands. Commands take two longs and are structured as hub_address + sdram_address. To have
the driver perform a read or write operation on the SDRAM, first set the sdram_address (2nd long) and then the hub_address
(1st long). The driver will set each hub_address long to skip_done (0) when its associated operation has completed.
Before starting the driver, build the command list structure with pairs of 0's, then terminate it with an 8. When launching
the driver into a cog, point the parameter (S) to the start of the command list.
Command list (longs):
hub_address (skip_done/rw_xxxx)
sdram_address
hub_address (skip_done/rw_xxxx)
sdram_address
hub_address (skip_done/rw_xxxx)
sdram_address
...
end_of_list
}
DAT org
'
'
' Initialize SDRAM clocks hub
' ----------------
sdram_driver getptra list '1 - save command list address
reps #6000,#1 '1 - repeat instruction for 100us @60MHz
mov pinc,h003FFFFF '1 - cke and cs high
mov dirc,h003FFFFF wz '6000 - set SDRAM signals to outputs (100us), Z = 0
'
'
' Scan list for commands
'
:finish if_z wrlong cmd,ptra[-2] '1 0 if command finished, set it to skip_done (cmd = 0)
mov pinc,h00240400 '1 1 issue 'precharge all' command
setb pinc,#20 wz '1 2 deselect, Z = 0
:reset if_z setptra list '1 3 if end_of_list, reset list pointer; satisy Trp
mov pinc,h00200037 '1 4 issue 'set mode' command
setb pinc,#20 '1 5 deselect, satisfy Tmrd
mov pinc,h00226000 '1 6 issue 'auto refresh' command
setb pinc,#20 '1 7 deselect, satisfy Trfc in 8 more clocks
:next rdlong cmd,ptra++[2] wz '3 0..2 check command, point to next
setptrb cmd '1 3 in case command, set hub address
if_z jmp #:next '1/4 4/4..7 if skip_done, next command
decod3 cmd wc '1 5 decode size bits, C = write
and cmd,#%11111110 wz '1 6 isolate bits 7..1, Z = 1 if end_of_list
if_z jmp #:reset '1/4 7/7..2 if end_of_list, reset list pointer
'
'
' Execute read/write command clocks hub XFR write SDRAM XFR read SDRAM
' ------------------------------------------------------
rdlong adr,ptra[-1] '3 0..2 - - get SDRAM address
shl adr,#7 '1 3 - - make 'active' command with bank and row address
or adr,#%1_0011_00 '1 4 - - (ba[1..0], a[12..0]) = adr[24..10]
rol adr,#15 '1 5 - -
mov pinc,adr '1 6 - - issue 'active' command
setb pinc,#20 '1 7 - - deselect, satisfy Trcd in 1 more clock
if_c rdquad ptrb++ '1 0 - - if write, read initial QUADs from hub
setbc :quad,#0 '1 1 - - set wrquad/rdquad according to read/write
if_c setxfr #%010_011 '1 2 <QUADs_to_16_pins> - if write, configure XFR at hub cycle 2
shr adr,#22+1 '1 3 - - make blank command with bank and column address
and pinc,h00306000 '1 4 - - (ba[1..0], a[12..0]) = (adr[24..23], %0000, adr[9..1])
or pinc,adr '1 5 - -
if_c mov dirb,hFFFF0000 '1 6 - - if write, enable data outputs
if_nc setxfr #%100_011 '1 7 <16_pins_to_QUADs> if read, configure XFR at hub cycle 7
if_nc xor pinc,h001A0000 '1 0 - - if read, issue 'read' command
if_nc setb pinc,#20 '1 1 - - if read, deselect
if_c xor pinc,h00180000 '1 2 - - if write, issue 'write' command (aligns with XFR on next clock)
if_c setb pinc,#20 '1 3 output QUAD0 w0 - if write, deselect; read: SDRAM sees 'read' command
shr cmd,#1 '1 4 output QUAD0 w1 - get loop count
if_nc nop #7 '1 5 output QUAD1 w0 - if read, pad time
'(nop) '1 6 (skip to next hub 6) - read: SDRAM starts outputting data stream
'(nop) '1 7 -
'(nop) '1 0 input w0 read: SDRAM data stream begins arriving in XFR
'(nop) '1 1 input w1 -> QUAD0
'(nop) '1 2 input w0
'(nop) '1 3 input w1 -> QUAD1
'(nop) '1 4 input w0
:quad '(rdquad/wrquad) '1 5 output QUAD1 w0 input w1 -> QUAD2
'(rdquad/wrquad) '1 6 output QUAD1 w1 input w0
'(rdquad/wrquad) '1 7 output QUAD2 w0 input w1 -> QUAD3
rdquad ptrb++ '1 0 output QUAD2 w1 input w0 write: read next QUADS; read: write current QUADS
djnz cmd,#:quad '1 1 output QUAD3 w0 input w1 -> QUAD0 loop for each set of QUADs
'(djnz looping) '1 2 output QUAD3 w1 input w0
'(djnz looping) '1 3 output QUAD0 w0 (new) input w1 -> QUAD1 write: RDQUAD data valid
'(djnz looping) '1 4 output QUAD0 w1 input w0
mov pinc,h002C0000 '1 2 output QUAD3 w1 - issue 'burst terminate' command (aligns with XFR on next clock)
mov dirb,#0 wz '1 3 - - cancel data outputs (aligns with 'burst terminate'), Z=1
jmp #:finish '4 4..7 - - finish up, scan for next command
'
' Constants
'
h00180000 long $00180000 'write' command toggle
h001A0000 long $001A0000 'read' command toggle
h00200037 long $00200037 'set mode' command (full-page r/w bursts, cas latency = 3)
h00226000 long $00226000 'read' command mask
h00240400 long $00240400 'precharge all' command
h002C0000 long $002C0000 'burst terminate' command
h00306000 long $00306000 'blank command mask
h003FFFFF long $003FFFFF 'control pins mask
hFFFF0000 long $FFFF0000 'data pins mask
'
'
' Variables
'
list res 1 'command list pointer
cmd res 1 'command
adr res 1 'address
'
'
'************************
'* *
'* Graphics *
'* *
'************************
'
' The following program uses SDRAM driver command slot 1 to write the video display (640x480 longs)
'
org 'graphics driver
graph setpix pix_ 'set 1x1 1-bit texture
setpixr pixrgb 'reset pix rgb scaling registers
setpixg pixrgb
setpixb pixrgb
pusha color0 'install texture colors into stack ram at $00 and $01
pusha color1
setquad #0 'init QUADs pointer to #0
fixinda #3,#0
'
'
' Draw screen left-to-right/top-to-bottom
'
:pixel mov xx,xpos 'compute complex arctan of coordinates
sub xx,#640/2
mov yy,ypos
sub yy,#480/2
qarctan xx,yy
getqx xx
getqz zz
max xx,#511 'make luma from length
shl xx,#23
testb xx,#31 wc
setbc texture,#0 'set black/white
qsincos xx,hsincos 'convert length to sinusoid
getqx yy
add yy,#$80
shl yy,#8 'set alpha blend
setpixa yy
shl yy,#16 'swirl colors
add zz,yy
call #chroma 'make chroma from angle
mov pix,z
add zz,h55555555
call #chroma
shl z,#8
or pix,z
add zz,h55555555
call #chroma
shl z,#16
or pix,z
nop #2 'blend chroma with black or white
nop #2
getpix pix
call #plot 'plot pixel
if_nc jmp #:pixel 'if more pixels, loop
jmp #$
'
'
' Get chroma from 32-bit angle
'
chroma qsincos zz,hsincos
getqx z
add z,#$80
chroma_ret ret
hsincos long 77 '127 * 0.607
h55555555 long $55555555 '1/3 circle
'
'
' Plot next pixel, C=1 if end of screen
'
plot mov inda++,pix 'store pixel in QUADs
incmod qptr,#3 wc 'QUADs full?
if_nc jmp #plot_ret 'if not, return
:wait rdlong z,ptra[2] 'QUADs full, wait for last command to finish
tjnz z,#:wait
wrquad pcmd 'write QUADs
wrlong pptr,ptra[3] 'write SDRAM address
wrlong pcmd,ptra[2] 'write SDRAM command
add pptr,#$10 'update SDRAM address
plot_ret retd
incmod xpos,xlim wc 'increment position
if_c incmod ypos,ylim wc
if_c mov pptr,#0 'if end-of-screen, reset pixel pointer
'
'
' Variables
'
pcmd long $1409
pix long 0
z long 0
xx long 0
yy long 0
zz long 0
pptr long 0
qptr long 0
xlim long 640-1
ylim long 480-1
xpos long 0
ypos long 0
pixrgb long $0000_FF00
color0 long $FF_00_00_00 'solid black
color1 long $FF_FF_FF_FF 'solid white
' 1 x 1 map, 1-bit pixels, no mirroring
pix_ long %000_000_00_0_00 << 21 + $00 << 9 + texture
texture long 0 '1x1 1-bit texture
I'd put a picture of it up, but my camera's batteries are dead.
Comments
Maybe someone can take a screen shot.
I guess you're saying that at 180 MHz clock there'd be time during refresh or something to update SRAM?
No chance of changing SRAM during active display time right? Or, is there?
Could the DE0 show color bars or something in this mode?
Woops! Never mind that .BIN file. I meant to load the .SPIN file there. I'll just delete the .BIN and you can copy and paste that source code into PNUT to run it.
There's no time at 60MHz to update the display, but at 180MHz, you could write it twice over for every display period.
The VGA demo I made a while ago does color bars, but has no need for all-points-addressable graphics, so it runs on the DE0.
Thanks for posting that pic.
Tonight I'll reduce the resolution to 320x240 so that I can potentially write the bitmap 3x over per display period. Then, I'll have the graphics cog animate the colors and patterns.
I was surprised I was able to get such rich analog-looking effects using a few CORDIC functions. Potatohead, Pedward, Sapieha, all you with DE2-115's, have you run this program and played around with the code that draws the image? Having transcendental functions in hardware makes wild things possible.
You could always send me one, I'm at my limit of what I can develop in one COG, since I've been hitting a lot of cycle limits on what I've been tinkering with. I'll post my color TTY text driver in a moment and give some details on what I learned.
I have started and look --- But not played with it.
My Basic eat up all my time.
SDRAM_Graphics6.spin
It renders 26 frames into SDRAM which takes ~20 seconds before you get a stable image. It uses almost all of the 32MB SDRAM.
I don't remember where I put it and I'm not in the office right now, so I'm unable to repost it. I'll post it to a sticky thread later today.
I have:
69f9b0e3a64744c08723feef212d7349 ./PNut.exe
69f9b0e3a64744c08723feef212d7349 ./tty/PNut.exe
69f9b0e3a64744c08723feef212d7349 ./baggers/DE0/PNut.exe
69f9b0e3a64744c08723feef212d7349 ./baggers/JB_Sprites_DE0/PNut.exe
That's the MD5 sum of the file.
I think You look on that instruction.
000011 ZCN 1 CCCC nnnnnnnnn 010100000 | NOPX | D/#n | Repeat the NOP instruction the value in register D(0-511) | or n(0-511) times. (Time delay)
if_nc nop #7 '1 5 output QUAD1 w0 - if read, pad time '(nop) '1 6 - read: SDRAM starts outputting data stream '(nop) '1 7 - '(nop) '1 0 input w0 read: SDRAM data stream begins arriving in XFR '(nop) '1 1 input w1 -> QUAD0 '(nop) '1 2 input w0 '(nop) '1 3 input w1 -> QUAD1 '(nop) '1 4 input w0
PNut I have compile that.
PNut_20121221.zip
As soon as I know which is the latest, I will update the sticky with a link to it.
However it doesn't support NOPX, which is supposed to be the same instruction.
http://youtu.be/vS8jVrSbs7k
Sorry for the bad quality, my camera refuses to autofocus on the LCD screen
I finally had time to re-flash my de2...
Your demo is amazing!
I think I'll have some time tomorrow to start on some high resolution SDRAM drivers