SRAM VGA text mode module added to p1v - now with high-level Spin driver
pik33
Posts: 2,416
I did first tests with this module. This is text mode driver for 1440x900 resolution. Active screen is 1280x800, 50 lines of 160 characters, 8x16 pixels, attached to the Propeller via extended portb
The memory map is: 8000 words from 0 - character/attribute (as in CGA)
$FFFF0 - border R
$FFFF1 - border G
$FFFF2 - border B
$FFFF3 - cursor position
The rest of 2 MB SRAM chip is free for any use.
To write the data put the address on dirb and the 16-bit data on outb, then strobe the data with dirb[31]. To read the data put the address on dirb, then read the data from inb.
Test program:
The memory map is: 8000 words from 0 - character/attribute (as in CGA)
$FFFF0 - border R
$FFFF1 - border G
$FFFF2 - border B
$FFFF3 - cursor position
The rest of 2 MB SRAM chip is free for any use.
To write the data put the address on dirb and the 16-bit data on outb, then strobe the data with dirb[31]. To read the data put the address on dirb, then read the data from inb.
Test program:
pub testvga |i,j,k
dirb:=$FFFF0
outb:=0
dirb[31]:=1
dirb[31]:=0
dirb:=$FFFF1
outb:=$40
dirb[31]:=1
dirb[31]:=0
dirb:=$FFFF2
outb:=$80
dirb[31]:=1
dirb[31]:=0
dirb:=$FFFF3
outb:=$0101
dirb[31]:=1
dirb[31]:=0
repeat j from 0 to 49
repeat i from 0 to 159
dirb:=i+160*j
outb:=0
dirb[31]:=1
dirb[31]:=0
repeat j from 0 to 49
repeat i from 0 to 159
dirb:=i+160*j
outb:=i<<8+j
dirb[31]:=1
dirb[31]:=0

Comments
Dr Acula, of course the 1M16 SRAM on the DE2-115 can be used for full graphics, I have done that in my Soft Cog project. There are 2 problems however that need to be solved in the Verilog code:
- This memory is slower that the internal M9K memory blocks, I have used it at 40MHz only (for a 800x600 VGA display, one byte per pixel)
- You have to time-multiplex the memory between write-access (from the user) and read access (from the VGA controller). One time slot for write access, one timeslot to read 2 pixels
The internal M9K blocks are dual ported by nature so read and write access can be independant and the Verilog can be much simpler.
Yes, it can do, I have a 800x600x24 graphic driver, but it is extremely slow. The vga get all bandwidth and you can access the RAM olly during h/vblank.
It seems to be possible making a palette registers for this.
It is possible, with the same timings, do an 1280x800 graphics in a 1440x900 screen. It reads two bytes and makes 8 pixels, so we can have 1280x800x4 colors. To do.
pik33
Thank you for the excellent sources and explanations. I have about 2 days of actual verilog study. Mostly browsing.
I am running 32bit Quartus 13.0.1 on an XP machine, using SystemVerilog.
When I try to compile, it stops almost immediately with 3 errors.
The first seems to be the issue:
"Illegal Assignment : IP_Generated_Device family. Specify a legal assignment name"
I thought that I had a problem in Assignments->Devices, but that appears to be ok.
Thanks,
Rich
Download again the project and delete project file prop-vga.qpf. Then make a new project for de2-115. Use prop-vga.bdf as main entity. Import the assignments from DE2_115_pin_assignments.mod.csv. Set in fitter option "aggressive routability optimizations" - always or it will compile forever hanging on 72..80%. Maybe you will have to remove pll and create it again. It should output 212.5 MHz.
- the Propeller clock is synchronized with pixel clock (106.25 MHz). Not synchronized clock causes random errors when scrolling up/down
- RGB components was changed so color 1 is blue and 4 is red, to meet CGA standard color map
Edit: I uploaded rs-232 programmable version of it. There are green labels under UART pins. Replace the UART_xxx with GPIO[xxx] as in these labels to make the project programmable with a Propplug
Edit 2: Added a PropPlug version
CON _clkmode = xtal1+pll16x _clkfreq = 106_250_000 '******************************************************************************************** ' Nostalgic VGA - FPGA version ' Version 0.01 alpha - 25.08.2014 ' No asm cogs used, any freq will do :) ' 160x50 text with border, 8x16 font ' (c) 2014 Piotr Kardasz pik33@o2.pl ' MIT license: see end of file '******************************************************************************************** var byte n_string[12] ' string buffer for inttostr and hextostr byte currentcolor byte cursorx byte cursory byte cursor_on byte screencolors pub demo | random, i, j, s1,s2,s3,s4,s5,s6,s7,s8 s1:=string("VGA Nostalgic 160x50 text with border driver demo") s2:=string("It uses Atari ST mono 8x16 font") s3:=string("Every letter can have its own foreground and background color out of 16 standard CGA colors") s4:=string("Scrolldown function, still too slow ") s5:=string("The framebuffer is located in DE2-115 SRAM and accessed via extended port B") s6:=string("You can set colors for all screen using one function call") s7:=string("You can set border color for all screen using one function call, too") s8:=string("We can draw a box") j:=0 repeat cls currentcolor:=0 setscreencolors (14,1) setbordcolors (0,0,192) cursoroff outtextxy (80-(strsize(s1)>>1),10,s1) waitcnt(cnt+clkfreq<<1) setcurrentfontcolor (12,1) outtextxy (80-(strsize(s2)>>1),12,s2) waitcnt(cnt+clkfreq<<2) setscreencolors (15,1) j:=3 repeat i from 0 to 7999 if (j>127) j:=2 dirb:=i outb:=(inb&$FF)+(j<<8) dirb[31]:=1 j:=j+1 dirb[31]:=0 waitcnt(cnt+clkfreq<<2) repeat j from 0 to 3 scrolldown outtextxy (80-(strsize(s4)>>1),2,s4) repeat j from 3 to 49 scrolldown waitcnt(cnt+clkfreq<<2) scrolldown setcurrentfontcolor (14,1) outtextxy (80-(strsize(s3)>>1),10,s3) waitcnt(cnt+clkfreq) outtextxy (80-(strsize(s5)>>1),12,s5) waitcnt(cnt+clkfreq<<3) j:=3 repeat i from 0 to 7999 if (j>127) j:=2 dirb:=i outb:=j&255+(j<<8) dirb[31]:=1 j:=j+1 dirb[31]:=0 waitcnt (cnt+_clkfreq<<3) cls currentcolor:=0 cursoroff setscreencolors (14,1) setbordcolors(0,0,192) outtextxy (80-(strsize(s8)>>1),10,s8) waitcnt(cnt+clkfreq) box(20,15,140,48) waitcnt (cnt+_clkfreq<<3) cls currentcolor:=0 cursoroff setscreencolors (14,1) setbordcolors(0,0,192) outtextxy (80-(strsize(s6)>>1),10,s6) waitcnt(cnt+clkfreq) random:=cnt repeat j from 0 to 15 waitcnt (cnt+_clkfreq>>1) setscreencolors (random? & 15,random? & 15) setscreencolors (14,1) outtextxy (80-(strsize(s7)>>1),12,s7) waitcnt(cnt+clkfreq) repeat j from 0 to 15 waitcnt (cnt+clkfreq>>1) setbordcolors (random? & 255,random? & 255,random? & 255) waitcnt (cnt+clkfreq>>1) setbordcolors(0,0,192) cls setscreencolors (14,1) cursoron repeat i from 0 to 10 writeln(s1) writeln(s2) writeln(s3) writeln(s4) writeln(s5) writeln(s6) writeln(s7) writeln(inttostr(12345678)) writeln(hextostr(12345678)) waitcnt(cnt+clkfreq<<2) setscreencolors (14,1) setbordcolors (0,0,1) cls '******************************** End of demo ************************************************************************ '********************************************************************************************************************* '************************************************************************* ' * ' Cursor functions * ' * '************************************************************************* pub cursoron '************************************************************************* ' ' Switch cursor on ' Use after cursoroff to restore a cursor in its previous place ' '************************************************************************* setcursor(cursorx,cursory,1) pub cursoroff '************************************************************************* ' ' Switch cursor off ' To restore it, use cursoron ' '************************************************************************* setcursor(cursorx,cursory,0) pub setcursor(x,y,on) '************************************************************************* ' ' Set x,y position of cursor and switch it off/on ' '************************************************************************* cursorx:=x cursory:=y cursor_on:=on dirb:=$FFFF3 if on outb:=x<<8+y else outb:=255 dirb[31]:=1 dirb[31]:=0 pub setcursorx(x) '************************************************************************* ' ' Set x position of cursor ' '************************************************************************* cursorx:=x dirb:=$FFFF3 if cursor_on outb:=x<<8+cursory else outb:=255 dirb[31]:=1 dirb[31]:=0 pub setcursory(y) '************************************************************************* ' ' Set x position of cursor ' '************************************************************************* cursory:=y dirb:=$FFFF3 if cursor_on outb:=cursorx<<8+y else outb:=255 dirb[31]:=1 dirb[31]:=0 pub setcursorxy(x,y) '************************************************************************* ' ' Set x and y position of cursor ' '************************************************************************* setcursorx(x) setcursory(y) pub getcursorx return (cursorx) pub getcursory return (cursory) pub backspace cursorx -=1 if cursorx<0 cursorx:=0 cursory-=1 if cursory<0 cursory:=0 putchar(cursorx,cursory,32) setcursorxy(cursorx,cursory) '************** End of cursor functions ********************************** '********************************************************************************* ' * ' Text functions * ' * '******************************************************************************** pub outtextxy(x,y,text) | b, base, i '******************************************************************************** ' ' Output string at position x,y ' '******************************************************************************** base:=x+160*y b:=strsize(text)-1 repeat i from 0 to b bytemove (@base,text+i,1) putchar(x+i,y,base) pub putchar(x,y,charcode) |b '******************************************************************************** ' ' Output a character at position x,y ' '******************************************************************************** dirb:=160*y+x if currentcolor==0 outb:=(inb&$FF) | (charcode<<8) else outb:=currentcolor+charcode<<8 dirb[31]:=1 dirb[31]:=0 pub box(x1,y1,x2,y2) |i '******************************************************************************** ' ' Draw a box at position x1,y1,x2,y2 ' '******************************************************************************** putchar(x1,y1,10) putchar(x2,y1,9) putchar(x1,y2,12) putchar(x2,y2,11) repeat i from x1+1 to x2-1 putchar(i,y1,3) putchar(i,y2,3) repeat i from y1+1 to y2-1 putchar(x1,i,4) putchar(x2,i,4) pub write(text) |b, c_x,c_y,l,i,lines '******************************************************************************** ' ' Output string at cursor position ' Set new cursor position at the end of string ' '******************************************************************************** c_x:=cursorx c_y:=cursory l:=strsize(text) b:=80*c_y+c_x if ((b+l) > 8000) lines:=1+ ((b+l-8000) / 1600) repeat i from 1 to lines scrollup c_y:=c_y-1 b:=b-160 outtextxy(c_x,c_y,text) c_x:=(b+l)//160 c_y:=(b+l)/160 setcursorxy(c_x,c_y) pub writeln(text) |b, c_x,c_y,l,i,lines '******************************************************************************** ' ' Output string at cursor position ' Set new cursor position at beginning of new line ' '******************************************************************************** c_x:=cursorx c_y:=cursory l:=strsize(text) b:=160*c_y+c_x if ((b+l) > 8000) lines:=1+ ((b+l-8000) / 160) repeat i from 1 to lines scrollup c_y:=c_y-1 b:=b-160 outtextxy(c_x,c_y,text) c_x:=(b+l)//160 c_y:=(b+l)/160 if (c_x <> 0) c_y+=1 c_x:=0 if (c_y==50) c_y:=49 scrollup setcursorxy(c_x,c_y) pub inttostr(i) |q,pos,k,j '******************************************************************************** ' ' Convert integer to dec string ' Return a pointer ' '******************************************************************************** j:=i pos:=10 k:=0 if (j==0) return string ("0") if (j<0) j:=0-j k:=45 n_string[11]:=0 repeat while (pos>-1) q:=j//10 q:=48+q n_string[pos]:=q j:=j/10 pos-=1 repeat while n_string[0]==48 bytemove(@n_string,@n_string+1,12) if k==45 bytemove(@n_string+1,@n_string,12) n_string[0]:=k result:=@n_string pub hextostr(i) |q,pos,k,j '******************************************************************************** ' ' Convert integer to hex string ' Return a pointer ' '******************************************************************************** j:=i pos:=10 k:=0 if (j==0) return string ("0") if (j<0) j:=0-j k:=45 n_string[11]:=0 repeat while (pos>-1) q:=j//16 if (q>9) q:=q+7 q:=48+q n_string[pos]:=q j:=j/16 pos-=1 repeat while n_string[0]==48 bytemove(@n_string,@n_string+1,12) if k==45 bytemove(@n_string+1,@n_string,12) n_string[0]:=k result:=@n_string pub cls | i '********************************************************************************** ' ' Clear screen, filling it with spaces and not touching any colors ' Set cursor at 0,0 ' '********************************************************************************** repeat i from 0 to 7999 dirb:=i outb:=0 dirb[31]:=1 currentcolor:=$00 dirb[31]:=0 setcursor(0,0,1) pub scrollup | i '********************************************************************************** ' ' Scrolls screen one line up ' '********************************************************************************** repeat i from 160 to 7999 dirb:=i outb:=inb dirb:=(i-160) |$8000_0000 outb:=screencolors repeat i from 7840 to 7999 dirb:=i dirb[31]:=1 dirb[31]:=0 pub scrolldown | i,t '********************************************************************************** ' ' Scrolls screen one line down ' '********************************************************************************** repeat i from 0 to 7839 dirb:=7839-i outb:=inb dirb:=(7999-i)|$8000_0000 outb:=screencolors repeat i from 0 to 159 dirb:=i dirb[31]:=1 dirb[31]:=0 pub setbordcolors(r,g,b) |color, i, temp '******************************************************************************* ' ' Set border color for all screen ' '******************************************************************************* dirb:=$FFFF0 outb:=r dirb[31]:=1 dirb:=$FFFF1 outb:=g dirb[31]:=1 dirb:=$FFFF2 outb:=b dirb[31]:=1 dirb[31]:=0 pub setscreencolors(f,b) | i screencolors:=b<<4+f repeat i from 0 to 7999 dirb:=i outb:=(inb & $FF00) |screencolors dirb[31]:=1 dirb[31]:=0 pub setcurrentfontcolor(f,b) currentcolor:=b<<4+f dat '******************************************************************************** 'Unimplemented in this version '******************************************************************************** { pub setcursorshape(l1,l2,l3,l4) '************************************************************************* ' ' Define a cursor shape (16 bytes = 4 longs) ' '************************************************************************* cursoroff waitcnt(cnt+clkfreq/60) cursor_buf[0]:=l1 cursor_buf[1]:=l2 cursor_buf[2]:=l3 cursor_buf[3]:=l4 cursoron pub setblinkrate(rate) '************************************************************************* ' ' Define a cursor blink rate (in 1/30th of second) ' '************************************************************************* cursoroff waitcnt(cnt+clkfreq/60) cursor_blink_rate:=rate cursoron } '************************************************************************ {pub waitendvbl repeat until vblank<>0 repeat until vblank==0 } '************************************************************************* ' * ' Color functions * ' * '************************************************************************* { pub setscreencolors(fr,fg,fb,br,bg,bb) |font_color,back_color,i, temp '******************************************************************************* ' ' Set font and back colors for all screen ' '******************************************************************************* font_color:=fr<<6+fg<<4+fb<<2+3 back_color:=br<<6+bg<<4+bb<<2+3 temp:=back_color+font_color<<8+back_color<<16+font_color<<24 repeat i from 196 to 495 poke (i,temp) pub setbordcolors(r,g,b) |color, i, temp '******************************************************************************* ' ' Set border color for all screen ' '******************************************************************************* color:=r<<6+g<<4+b<<2+3 temp:=color+color<<8+color<<16+color<<24 repeat i from 187 to 195 poke (i,temp) pub setfontcolor(line,pos,r,g,b) | temp, place, color '******************************************************************************* ' ' Set colors for text at line (0..29) and position (0..19) ' and put them in the color buffer ' '******************************************************************************* color:=r<<6+g<<4+b<<2+3 place:=196+(pos>>1)+(line*10) if (place>495) place:=495 temp:=peek(place) if (pos//2==1) 'high byte temp:=temp & $00ffffff temp:=temp | (color<<24) if (pos//2==0) 'high byte temp:=temp & $ffff00ff temp:=temp | (color<<8) poke(place,temp) pub setbackcolor(line,pos,r,g,b) | temp, place, color '******************************************************************************* ' ' Set colors for background at line (0..29) and position (0..19) ' and put them in the color buffer ' '******************************************************************************* color:=r<<6+g<<4+b<<2+3 place:=196+(pos>>1)+(line*10) if (place>495) place:=495 temp:=peek(place) if (pos//2==1) 'high byte temp:=temp & $ff00ffff temp:=temp | (color<<16) if (pos//2==0) 'high byte temp:=temp & $ffffff00 temp:=temp | (color) poke(place,temp) pub setbordcolor(line,r,g,b) | temp, place, color '******************************************************************************* ' ' Set colors for bborder at line (0..29) ' and put them in the color buffer ' '******************************************************************************* color:=r<<6+g<<4+b<<2+3 place:=188+(line>>2) if(line>29) place:=187 line:=line-30 if (place>495) place:=495 temp:=peek(place) if (line//4==0) temp:=temp & $ffffff00 temp:=temp | (color) if (line//4==1) temp:=temp & $ffff00ff temp:=temp | (color<<8) if (line//4==2) temp:=temp & $ff00ffff temp:=temp | (color<<16) if (line//4==3) temp:=temp & $00ffffff temp:=temp | (color<<24) poke(place,temp) pub defchr(code) |i '********************************************************************************** ' ' Redefine a character ' New char definition have to be in chardef table '********************************************************************************** repeat i from 0 to 15 byte[@st_font+code<<4+i]:=chardef[i] pub poke(addr,val) '******************************************************************************** ' ' Insert a long into display cog ram ' '******************************************************************************** cmd2:=val cmd1:=addr repeat until (cmd1 == $FFFFFFFF) pub peek(addr) '******************************************************************************** ' ' Get a long from display cog ram ' '******************************************************************************** cmd1:=addr+$FF000000 repeat until (cmd1 == $FFFFFFFF) return cmd2 } {{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ TERMS OF USE: MIT License │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ │files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ │modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ │is furnished to do so, subject to the following conditions: │ │ │ │The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ │ │ │THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ │WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ │COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ │ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }No more floating pixels in the first column of active screen. This was the case when "=" was useful.
This is rs232 programmable version. To make it propplug programmable, replace the UART_xxx with GPIO[xxx] as in the green labels near UART pin and recompile. The RS232 cable seems to be more stable thing than the propplug inserted into GPIO with an USB cable attached to it
There should be a way to make the circuit programmable with both methods - RS232 and Propplug@GPIO, but first try didn't work, the Propeller was not programmable at all.
There is Spin driver in the project directory. Now it uses pasm for fast scrolling, color changing and clearing the screen
I added some internal RAM to store border colors and cursor position (the rest of 64 word block is now unused). The internal RAM is accessible with addresses with bit 20 set.
The Propeller works now @ 125 MHz
Thanks again for your posts. I particularly like how you have configured dirb and outb. In this sense your code serves as a template for experimentation.
I upgraded to Quartus II ver 14. I am now able to compile without errors... but there is no .cof file.
It occurred to me that I might have my settings wrong or that I am missing something.
I searched the web and looked through Quartus documentation mostly looking for causes of missing .cof files to no avail.
Any help would be appreciated.
Rich
You need .sof or .pof to program the device. You can find it in the project directory or in its output_files folder if it exists.
Now I am working at graphics driver with the same inb/outb/dirb configuration. Some work still needed before I will put it here.
This would be a generalized memory controller... all available through the same interface...the beauty of it all:)
There are no good examples that I can find anywhere for either SDRAM or embedded memory that doesn't use either IP or NIOSII.
I found this link helpful:
http://quartushelp.altera.com/12.1/mergedProjects/reference/glossary/def_file_types.htm
I am looking once again, trying to gain a better understanding. I looked through the files trying to figure out how you hooked up outb and dirb to the SRAM. The only connection I see is in the block diagram... is this correct? Nothing in the .v files that I missed?
By the way, I am just a little bit familiar with VGA, it is really fascinating to see how it is done in Verilog... pretty snazzy.
Thanks
When I open st4font.hex as a binary image 32 X 2048 I don't see the final font representation... how are the bits in the hex values computed?
Ports are connected to vga module in the block diagram, so there is no verilog code for this connection.
VGA verilog module gets input from the Propeller via this connection and then generates signals for SRAM chip. This is done in verilog inside the VGA module
Font is 8x16 pixels, 128 characters defined, one byte is one line of character, 1 bit=1 pixel. The font is ripped from Atari ST and it is the same font I used in my Propeller vga "nostalgic" driver
Thank you... again and again. This is as I understood the connections and code. But I just was not confident.
The code is very clear once the connections in the block diagram are considered.
I will have to go back later and look at the font again. I am not on my home computer right now.
This is beyond me at the moment, would it be possible to use m9k memory blocks as a dual ported screen buffer... so that the VGA display could have full time read access and writing to the buffer by portb could be done simultaneously?
Regards,
Rich
It is possible and it is much simpler to do than fighting with SRAM timings and signals. I rewrote this driver for DE1-SoC. This board doesn't have SRAM so I simulated it with its internal memory blocks. No problems with timings, no problem with addressing. The problam was when I tried to compile this for the first time: it compiles forever and eats all 8 GB of RAM i have in my computer. The solution was using RAM megafunction: I created RAM module with the megafunction and then connected it to the VGA driver instead of SRAM. Then it compiles in less than 10 minutes.
This was one port RAM - to simulate a real SRAM and don't change driver's timings.
Full writing acces is not needed. The Propeler needs at least 8 instruction to effective write to SRAM (including loop, rdlong or calculating next value). This is over 24 pixels. The Propeller has acces to SRAM every 8 pixels.