C-One Tutorial
Videooutput using framebuffers
This tutorial includes the first part of a design for a framebuffer. A framebuffer stores an image that is to be displayed on an monitor. For the first tutorial, we will lock at the output of an image stored in a buffer to the monitor. The image will be read from the static RAM on the CPU card of the C-One. The next tutorial will contain a manager for the memory, that will allow read- and write- access to the framebuffer. This tutorial is based on the pong tutorial by Tobias Gubener.
Choosing a videoformat.
The first question that must be answered is, what video format do we use for our framebuffer. As this tutorial will be the basic design for an ZX81 clone, we don't need any color. The original ZX81 has only an output of 24 lines with 32 characters per line. The characters are composed of 8*8 pixels each. That gives us a resolution of 256*192 pixels. This isn't enough for actual monitors. So we choose one of the lowest possible resolutions to be used. We well use a display resolution of 640*480 pixels at 60 HZ.
Now we need to know how the Image must be send to the Monitor. On a standard Monitor the Image will be drawn by a beam of electrons. Those electrons are emitted from an electron cannon at the end of the vacuum tube of the Monitor. When the electrons from the cannon hits the screen, light is emitted from a layer of phosphor. Magnets at the sides of the vacuum tube controls the aiming of the beam. The electronic of the monitor is setup to draw the Image line by line from top to bottom. At every line end the magnets that control the horizontal movement of the Beam need to change their polarity. And at the bottom of the screen the magnets that control the vertical movement of the electron beam also need to change their polarity. This changing of the polarity take some time. This times are called the vertical and horizontal blanking periods. Within this blanking periods the horizontal and vertical synchronization signals are used to synchronize the electron beam to the video signal.
To make the logic for our Videoout Entity simple we will thing of the Image to be larger than the visible area of 640 * 480 Pixels. So when we extend the Image by the horizontal and vertical blanking periods we came to an image size of 800 * 524 Pixels. Then we must now when to send the horizontal and vertical synchronization signals. There is an online calculator for all the needed information aviable here. The Picture below shows the whole image and where the synchronization signals are locatet.

Layout of the Image that's been send to the Monitor.
So we need to put out an Image of 800 * 524 Pixels to the Monitor. Also we want to put out this Image 60 times a Second. With a little Math we come to the conclusion that we need to put out a Pixel every 40ns. So our Design will need to be driven by a Frequency of about 25.17 MHz. Lucky we are given this Frequency by the Muxer-Part of the reference design.
Preparations for the Implementation
First we need some Images that have the right Format. As we have chosen an black and white Format, we need to pack 8 Pixels into 1 Byte. I made two Pictures for the use with the Democode. I made the Pictures using Paint-Shop-Pro-7. To get the needed Format i saved the Pictures an an unpacked TIF-Image. Then i used an hex editor to cut the Header an the Footer from the Image, so i got the Raw-Imagedata. The Raw-Imagedata than can be used as a ROM-Image, containing the Picturedata. As of a small Bug in the Bootloader the Size of an ROM-Image should be dividable by 4096 without a rest. So i filled up the Image with the Hex-Editor to a Filesize of 40960 Bytes.
Here are the Images and the converted ROM-Files.
Writing the Videooutput in VHDL
We will write the Part of the Videooutput with VHDL and integrate it into the reference Design from the Pong-Tutorial. The following Section describes the VHDL-Sourcecode of the Videooutput. You can find the Sourcecode of the VHLD-File here.
Include all nessesary Libraries:
Every VHDL-Code begins with the Inclusion of the necessary Library's. Here we choose the standard logic Library's.
-- Include the nessesary Libraries library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;
Description of the Interface:
The Interface of a VHDL-Design is defined within an entity Section of the VHDL-File. The Name of this Entity is video_out. This very simple Entity only consists of a List of the In- and Out-Signals. The Input- and Output-Signals are described by the definition of a port.
-- Definition of the Interface of the Module entity video_out is port ( reset : in std_logic; pixel_clock : in std_logic; pixel_out : out std_logic_vector ( 15 downto 0 ); hsync_out : out std_logic; vsync_out : out std_logic; addr_out : out std_logic_vector ( 16 downto 0 ); data_in : in std_logic_vector ( 7 downto 0 ); read_out : out std_logic ); end video_out;
The first two Inputsignals are reset and pixel_clock. The reset-Signal will be used to put the Videooutputmodule in a halted State. In this State there will be no Videooutput and no reading requests for the Ram. The Pixelclock-Signal will be used for the Timing of the In- and Output of the Videomodule. The Pixelclock- Signal is generated within the Muxer-Module. The Muxer-Module takes the Videosignal and forwards it via a special Graphicbus to the 1K30-FPGA. The 1K30-FPGA drives the Videooutput-DAC with the Videodata of the 1K100-FPGA. The Videodata expected by the Muxer has 16bits. So our Videoout-Signals also have 16 bits. The Monitor needs not only the 16bit Colorsignal but also the vertical and horizontal Sync-Signals. The Sync-Signals tells the Monitor when to begin a new Line ( horizontal Sync ) and when to begin a new Image ( vertical Sync ). Therefore are the Signals hsync_out and vsync_out defined. The last three defined Signals will be used to read the Imagedata from the Ram on the CPU-Card. The Signal addr_out contains 17 Bits of Adressdata. With this 17 Bits of Addressdata the 128KB of the Ram on the CPU-Card can be fully addressed. To enable the Output of Data from the Ram to the Databus we define the Signal read_out. The Ram puts the Data addressed by addr_out onto the Bus when the read_out Signal is driven from High to Low. To read the Data from the Bus into the Videoutput-Module we define data_in as an 8bit Input-Signal.
Architecural Description
The Description of the Architecture describes the Logic that should be represented by the Design. The Description of the Videooutputmodule is called behavior. The definition of the Name and the Reference to the entity is followed by the definition of all globally used Signals of the Videoout-Entity. This Signals are only visible by the Processes defined for this Entity.
-- Description of the Logic of the Videomodule architecture behavior of video_out is signal h_counter : std_logic_vector (9 downto 0); signal v_counter : std_logic_vector (9 downto 0); signal blank : std_logic; signal hsync_sig : std_logic; signal pixel_addr : std_logic_vector ( 16 downto 0 ); signal pixel_data : std_logic_vector ( 7 downto 0 ); signal next_data : std_logic_vector ( 7 downto 0 ); begin
The first two defined Signals h_counter and v_counter will be the Counters for the position of the Videooutput. Their Values define the actual Pixelposition of the Videooutput. The blank Signal will be used to store a boolean Variable. This Boolean will tell the Outputprocess to stop sending out Pixels of the Image when the Rasterbeam of the Monitor leaves the visible Part of the Screen. The Signal hsync_sig is used to tell the vertical Counter when a Line of the Screen ends, so it can increase the Linecounter. The Signal pixel_addr will hold the Address of the next Byte that's to be read from the RAM. The Signal pixel_data holds the Byte that is currently drawn to the Screen. Whereof next_data holds the next Byte that is to be drawn to the Screen.
The horizontal Pixelcounter
The first Process that's shown here is the Counter for the horizontal Pixelposition of the Pixel that's actually drawn to the Screen. Therefore it uses the Signal pixel_clock as a counting Signal for input. And as the Counter has to obay the reset Signal, it sets the Counter to zero as long as the reset Signal is 1. When the reset Signal is not 1 the Pixelclock Signal is to be evaluated. The Term pixel_clock'event means that the pixel_clock Signal changes it's State. And the Term pixel_clock ='1' means that the new State is 1. So everytime the pixel_clock Signal changes it's State to 1, the Counter will be updated. Before the Update the Value of the Counter will be Tested if it's less then 800. When the Counter's value is less that 800 the Counter will be increased by one else the Counter will be reset to zero.
-- Horizontal Pixel Counter
hpixel_count : process ( pixel_clock, reset )
begin
if reset = '1' then
h_counter <= "0000000000";
elsif ( pixel_clock'event and pixel_clock ='1') then
if h_counter < 799 then
h_counter <= h_counter + 1;
else
h_counter <= "0000000000";
end if;
end if;
end process;
The vertical Linecounter
The vertical Linecounter will hold the Number of the Line that's actualy to be drawn. To count the Lines we will use the Signal hsync_sig. This Signal will be set to '1' at the End of every Line, and it will be reset to '0' before the beginning of the next Line. So we can define it just like the horizontal Pixelcounter, but as of the Signal pixel_clock we will use the Signal hsync_sig instead. As we need to give out 524 Lines to the Screen the Counter will count to 524 and then will be reseted to zero. But where come the hsync_sig Signal from ? We need to define this the next.
-- Vertical Line Counter vline_count : process ( hsync_sig, reset ) begin if reset = '1' then v_counter <= "0000000000"; elsif ( hsync_sig'event and hsync_sig = '0' ) then if v_counter < 524 then v_counter <= v_counter + 1; else v_counter <= "0000000000"; end if; end if; end process;
Generation of the horizontal and vertical Sync-Signals.
The Monitor draws the Image Pixel per Pixel and Line per Line. And as the Monitor can display a large amount of different Resolutions we need to tell it where a Lines ends and when the last Line of the Display is being send. So it can adjust it's output to the send Data. For this reason a Monitor has two Inputs for Sync-Singals. On is called the horizontal Synchronisation Signal and the other is called the vertical Synchronisation Signal. This process will generate both Signals and it sends an extra signal to the vertical Linecounter at every end of the Line.
-- Generate horizontal and vertical Sync-Signals hsync_gen : process ( pixel_clock, reset ) begin if reset = '1' then hsync_out <= '1'; vsync_out <= '1'; hsync_sig <= '1'; elsif ( pixel_clock'event and pixel_clock = '1' ) then hsync_out <= '1'; vsync_out <= '1'; hsync_sig <= '1'; if ( h_counter > 686 and h_counter < 784 ) then hsync_out <= '0'; hsync_sig <= '0'; end if; if ( v_counter > 509 and v_counter < 512 ) then vsync_out <= '0'; end if; end if; end process;
Notice it's always good to set every Signal that's being written to in the conditional
statements to a default Value before the conditional Statements. So you don't get caught
with unexpected behaviors, when the Compiler generates also Logic for the cases not being
caught by the conditional Statements. So i set the Signals hsync_out,vsync_out
and hsync_sig in the above Example to a default Value of '1'.
The Blanking-Signal
The Blanking-Signal is used to tell the Output-Process when to send Pixeldata to the Monitor and when to wait while the Rasterbeam is out of the visible Area of the Screen. The logic for this behavior is simple as we must only Test if the horizontal Pixelcounter is greater than 639 or the Linecounter is greater than 479. Notice, that all Counters beginning their counting with 0. So the first Pixel of the visible Screen has a Zero in the h_counter and v_counter Signals.
-- Set Blanking State for later Use setblank : process ( reset, h_counter, v_counter ) begin if reset = '1' then blank <= '1'; else blank <= '0'; if ( h_counter > 639 or v_counter > 479 ) then blank <= '1'; end if; end if; end process;
Output of a Pixel to the Monitor
Once every Clockimpulse on the pixel_clock Signal we must Output a Pixel to the Monitor. But only when the blank Signal is low. So we setup the default logic to put out Zero on alls pixel_out Lines when in the Blanking periods. Also we define pixel_data<=pixel_data to tell the Compiler that we want a Register that holds the Pixeldata when we don't change it by the defined Logic.
-- One Bit per Pixel decoder and output. pixel_decode : process ( pixel_clock, reset ) variable tmp : std_logic_vector ( 7 downto 0 ); begin if reset = '1' then pixel_data <= "00000000"; pixel_out <= "0000000000000000"; elsif ( pixel_clock'event and pixel_clock = '1') then pixel_data <= pixel_data; pixel_out <= "0000000000000000"; if blank = '0' then if ( h_counter(2 downto 0) = "000" ) then tmp := next_data; else tmp (0) := '0'; tmp ( 7 downto 1 ) := pixel_data ( 6 downto 0 ); end if; if tmp ( 7 ) = '1' then pixel_out <= "1111111111111111"; end if; pixel_data <= tmp; end if; end if; end process;
We use the lower 3 Bits of the horizontal Counter to get the actual Bit that's to be output to the Screen. When the lower 8 bits of the horizontal Counter are "000" we need to get the next Byte we want to output. Otherwise we shift the actual pixel_data one Bit to the left. With every Clockimpulse we put out the 7. Bit of the pixel_data to the Screen. Since we defined the default Signal for pixel_out to be black we only need to define the Condition when to put out a white Pixel to the Screen. A white Pixel only will be output, when the blank Signal is low and the 7. Bit of pixel_data is high.
Notice, i used a the Variable tmp in the above process to tell the Compiler
that i need to write and read the updated Signal for pixel_data with the same
Clockimpulse. When we use only a Signal here, the Output won't be synchrony with the
blank Signal and the first and last columns of the Image would not be Displayed
right as the whole Image would be shifted one Pixel to the right.
Read one Byte from the Memory
The last Process for this part of the Design will be the readout of the next Byte from the Memory. To tell when to read the next Byte from Memory we will use the lower 3 Bits of h_counter and the blank Signal. We will give out the Address of the next Byte to be read with every Clockimpulse. So we can read the Data from the Memory when the lower Bits of h_counter are "010". When the lower Bits of h_counter are "100" we increase the Memoryaddress by one to point to the next Byte of Imagedata.
-- Read one Byte from Memory pixel_read : process ( pixel_clock, reset ) begin if reset = '1' then addr_out <= "00000000000000000"; pixel_addr <= "00000000000000000"; next_data <= "00000000"; read_out <= '0'; elsif ( pixel_clock'event and pixel_clock = '1' ) then addr_out <= pixel_addr; read_out <= '0'; pixel_addr <= pixel_addr; next_data <= next_data; if blank = '0' then if h_counter(2 downto 0) = "010" then next_data <= data_in; elsif h_counter(2 downto 0) = "100" then pixel_addr <= pixel_addr + 1; end if; if ( v_counter = 479 and h_counter = 629 ) then pixel_addr <= "00000000000000000"; end if; end if; end if; end process; end;
A special case is the End of the Imagedata. At the End of the Imagedata we need to reset the Addresscounter to Zero. And we need to do it, before the first Byte needs to be read. Therefore we reset the Addresscounter to Zero when the horizontal Counter reaches 479 and the vertical Counter reaches 629. When this is the case the lower 3 Bits from h_counter are "101". So it's always reset after the last Addresscounter incrementation.
Including the VHDL-Design into the Tutorial-Core of the Pong-Tutorial.
First you need to Download the Tutorial core of the Pong-Tutorial from here or from the Mirror on this Side. Extract the Files from the Pong-Tutorial into a new Directory. Then Copy the VHDL-Source of the Videooutput-Entity into the same Directory. Now we open up the Projectfile with the Altera Software or if you haven't started the Software by double clicking the File "a1k100.qbf" from the Project's Directory.
After we loaded the Project we will include the File "video_out.vhd" into our Project. We Choose "Add/Remove Files in Project" from the Project-Menu.

In the Dialog that opens we click on the Button "...", like shown below.

In the Filerequester that opens up we choose the File "video_out.vhd". Confirm your choice with the 'open' button, then leave the menu with the 'OK' button. Now Quartus knows that the VHDL design file is also part of the project.

Before you can place them in the toplevel design File, you need to generate a symbol block from the VHDL File. From the List of the Files choose the "video_out.vhd" File and right-click it. From the Pulldownmenu choose "Create Symbol Files for current File" like shown below.

Now the Compiler will generate a useable Symbol from our VHDL-File.
One nasty Bug i was searching was caused by the fact, that i changed the type of
an Outputsignal for an std_logic Type from "buffer" to "out", after i generated the
Symbol for it. And oops the changing of the Outputtype in the VHDL-File didn't made
it automatically into the Symbolfile at compile time. So when you change the Interface
of a VHDL-File just take a lock at the Symbol also and make sure, the changes got
there also.
Cleanup of the toplevel design File
Now we open up the toplevel File "a1k100.bdf" by double clicking it within the Filelist. You should see the Muxer-Symbol, the Symbol of the Sound-Entity some wias and some of the Pins of the 1K100 FPGA. Now Zoom in on the Muxer and the Soundsymbol. We would do a little clean up of this Part of the Design, because we don't need any Sound by now.
![]() |
![]() |
| bevore Cleanup | after Cleanup |
So delete the Sound-Enitiy by clicking on it and then pressing the "del"-Key on the Keyboard. Or by rightclick and choosing "delete" from the Pulldownmenu. Then delete all the wias that where connected to the Sound-Entity. Your design File should then lock like the right Picture from above now. After we deleted the Sound-Entity we do the same with the Logic of the RAM-IO, because at this Time we don't need write access to the RAM. We will only use a reading access of the RAM and I will replace the direct RAM access of the Entities with an new Entity witch controls read and write access to the RAM within the next Tutorials.
![]() |
![]() |
| bevore Cleanup | after Cleanup |
Now the part of to the right of the Muxer should lock like the right Picture from above. Only the Pins gb[5..0], 2MHz and be are still connected. The rest of the Pins to the right of the Design should have no more wires assigned to them. Now we can place the Video_Out Entity into our Design.
Insertion of the Video_Out Entity into the toplevel Design
After the cleanup of the toplevel design we can add the Symbol of the Videoout Entity to it. We will place the Symbol to the left of the Muxer. Just rightclick into the Designwindow and choose "Insert->Symbol".

From the Requester choose "Project" and then the "video_out" like shown below. Then click on "ok" and place the Symbol left below the Muxer.

Your design should like below now. In the next Part we will connect the Videoout Entity to the Muxer.

Connecting the Videoout Entity to the Muxer.
Now it's Time to connect the Videoout Entity to the Muxer. Use the Orthogonal Node Tool to connect pixel_clock with pixclkena of the Muxer. Then connect hsync_out to hsync and vsync_out to vsync. In the first Tutorial we don't use the Resetlogic of the Videoout Entity so we will connect the reset Signal to a GND-Pin. The GND-Pin is placed into the Design the same way you placed the Symbol for the Videoout-Entity. Just choose Insert->Symbol by rightclicking in the Designwindow then choose primitives->other->gnd from the Quartus2 Library. You also can type in gnd into the Namefield of the Dialog, to select the Groundpin. Place it like shown below into your Design and connect it to the reset input of the Videoout Entity.

Connection of the Videoout-Entity to the RAM-Bus
Also by now we don't have an Entity for the Memory Management in this Tutorial. So we will connect the Videoout-Entity to the RAM-Bus by some 'hand drawn' Logic. The Picture below shows the Logic we will use to access the RAM on the CPU-Card.

First we use the Orthogonal Bus Tool to draw a Bus from the Pin a[16..0]. We give this Bus the Name addr_out[16..0]. Also we Draw a Bus from the Pin mdb[7..0]. This Bus gets the Name data_in[7..0]. Because the Data-Bus is a Bidirectional-Bus we need to define an Logic for an Output. We use the Symbol primitives->buffers->tri from the Altera Quartus 2 Library. This Symbol defines a Tristate-Logic for the Dataoutput to the Bus. A Tristate Logic will connect the Inputbus of the Symbol to the Output only when the enable-Input of the Tristate-Symbol is being drawn to VCC. So when we connect both Inputs of the Symbol to Ground it never outputs anything to the Bus, but it pleases the Compiler that expects a Driver for the Dataoutput. The Output of the Tristate-Symbol should be connected to the Databus mdb[7..0] like shown above. As we don't write to the RAM, we connect the wr Pin of the Design to VCC. The needed Symbol can be find in the Altera Library under primitives->other->vcc. The RAM on the CPU-Card will be selected for writing and reading using the Pin ramcs. When the ramcs Pin is drawn low, the RAM will be selected for comunication over the BUS. For the ramcs Pin we need some special Logic. As long as the dma Input Pin is low, we are not allowed to use the BUS. So we will use an OR-Gate and an Inverter to connect the dma Pin and the read_out Pin of the Videoout Entity to ramcs. Connect the Signals like chown above. The Inverter and the OR-Gate are found in the Altera Library under primitives->logic->not and primitives->logic->or2. Your Design now should lock like the Image above. Now we need to connect the Buslines to the Videoout Entity and the dma Pin.
The Image below shows the final Design for the Videoout Entity. We will use the BUS and Wire- Tools to connect the Buslines, the DMA-Pin and the read_out Pin of the Videoout Entity. Use the BUS-Tool to draw a BUS at addr_out and Name it addr_out[16..0]. Then draw a Busline to data_in and Name it data_in[7..0]. Next use the Wire-Tool to draw a Wire at read_out and Name it read_out. At Last draw a Line at the dma Pin and Name it dma. Your Design around the Videoout Entity should lock like the Image below. Now we are finished with the toplevel Design and we are ready to compile it and make a Core for the C-One out of it.

Just remember never ignore the DMA-Input-Pin, or the BUS will be severely confused
and you end up with the contents of the Static-Ram to be overwritten with some
unknown Data.
Make a Core for the C-One
Now that all the Logic are defined and connected we can save and compile the Design. Just hit the Compilebutton and wait for the compiler to end. You will see about 49 Warnings but you should not see any Errormessages.
After the complitation take a lock at the hierarchy window. The numbers for the used logic cells should lock like in the picture below. When these numbers are smaler this is an indiaction that the compiler removed some of the logic from the final core. When this happens it's likely that you have a typo in the names of the busses or wias. Also typos in the VHDL-Source can lead the compiler to drop some logic. The compiler runs a test on the generatet logic. When the compiler means, that some of the defined logic states could not be reached it replaces the logic by static values. In an extreme case it can optimize all the logic away. This has happend to me by an error in the length of the horizontal pixelcounter. In on of my first tests i defined the counter to be a length of 9 bit. But when you lock at the source you will see, that i test the counter to be less than 800. Just with 9 bits the highest number can be 512, so the compiler says the signal h_counter never reaches 800. So there never is an end of line, so it can optimize the vertical counter v_counter away. And so it optimizes all processes that test the v_counter signal away. Not much was left of my logic, but you get not much more as a warning, because there was no syntax error in the VHDL file.
The Corefile for the 1K100 FPGA will be the File a1k100.rbf. It's been found in the Projectdirectory after the Compilation. Copy the File to a new Directory, then rename the Copy to 6fpga.bin. Then we Take our Testimage from the beginning and Name it 6r04000.bin Take the Raw-File, not the TIF or you will see a defective Image. Now use the Notepad to make the File 6desc.txt. The Textfile should contain the Name of the Core that's to be displayed in the Bootmenu. At last take a Copy of the File 0drive.bin from the C64-Core and rename it to 6drive.bin. Now copy all the Files to your CompactFlash-Card or witch Bootdevice you use. Boot up you C-One and choose the new Core. After the Setup the Image should be displayed onto your Monitor.
The first Character of the File is the Number of the Core and can be any Number between 0 and 9. Just use a free Number for your own Cores.
The Projectfiles for Download
Here are all the Files of the first Tutorial for download.
| Filedescription | Download |
|---|---|
| This is the QuartusII Project from this Tutorial. Included are the QuartusII Projectfiles and the Sourcecodes. | Quartus-Project |
| This is only the VHDL-File of the Videoout-Entity, that's also included in the Projectfile above. |
Video_out.vhd |
| This is a ready to Test Core with all Files needet. Theres a simple Testlogo included. Copy all Files to the Boot-Directory of your Bootdevice and you are ready for testing. | Democore |
| Simple diagonal Stripes. |
TIF-Image Striped-Picture in binary Format |
| Simple Logo. |
TIF-Image Logo-Picture in binary Format |



