Video
Author: Konrad Armbrecht 2025, Martin Erichsen 2025, Beaurel Ingride Ngaleu 2025,
Niklas Sirch 2026
Overview
The video module is split in two parts: a hardware and a software implementation. The hardware implementation provides a wishbone peripheral and outputs a VGA signal to an external monitor. The software implementation renders graphics in a framebuffer in main memory, which can be displayed in the GUI on the host machine.
Interface Definition
Overview
The Video module serves as the display output for the Piconut. This module provides a set of registers that facilitate the control and management of graphic rendering within the system. The Video adapter contains several registers, which are described below.
While the implementation of hardware or software video does not necessarily require the use of every feature of each register, it is essential that all registers are defined. This ensures that developers have a clear understanding of the available components and their functionalities, providing security and flexibility when developing video applications for the Piconut system.
Register offsets
The memory map for the Video control registers is shown in the table below. The Video memory map has been designed to require only 32-bit memory accesses.
Offset |
Name |
Description |
|---|---|---|
0x000 |
CONTROL |
Control register for the video interface [RW]. |
0x004 |
STATUS |
General status register. [RW implementation dependent] |
0x008 |
RESOLUTION_MODE |
Set value to choose resolution mode (bits select mode). [RW] |
0x00C |
RESOLUTION_MODE_SUPPORT |
Bitmask of supported resolution modes (1 = supported, 0 = unsupported). [R] |
0x010 |
COLOR_MODE |
Set bits to choose color mode (bits-per-pixel). [RW] |
0x014 |
COLOR_MODE_SUPPORT |
Bitmask of supported color modes (1 = supported, 0 = unsupported). [R] |
0x018 |
SCANLINE |
Current scanline being rendered. [R] |
0x01C |
FRAMEBUFFER_WIDTH |
Framebuffer width in pixels. [R] |
0x020 |
FRAMEBUFFER_HEIGHT |
Framebuffer height in pixels. [R] |
0x024 |
COLOR_MAP |
256 entries of 32-bit color values (mapped palette). [R] |
0x424 |
FRAMEBUFFER |
base address to framebuffer memory in the Piconut system (framebuffer area follows). [RW] |
Detailed register description
Note:
Attr. RW = Read and write
Attr. R = Read only
CONTROL Register
Register offset: 0x000 Can be used to control the graphic interface.
Bit |
Field name |
Attr. |
Description |
|---|---|---|---|
0 |
Interrupt |
RW |
Enable/disable Interrupt |
1 |
Output |
RW |
Enable/disable image output. |
STATUS Register
Register offset: 0x004 General status register for the graphic interface.
Bit |
Field name |
Attr. |
Description |
|---|---|---|---|
0 |
Ready |
R |
Indicates if the video interface is ready (0: not ready, 1: ready). |
1 |
Error |
R |
Indicates if there is an error (0: no error, 1: error occurred). |
2 |
Busy |
R |
Indicates if the video interface is busy (0: idle, 1: busy). |
RESOLUTION_MODE Register
Register offset: 0x008 Set register bit to choose respective resolution mode in pixel.
Bit |
Field name |
Attr. |
Description |
|---|---|---|---|
0 |
640x480 |
RW |
Resolution mode 640x480 pixel |
1 |
800x600 |
RW |
Resolution mode 800x600 pixel |
2 |
1024x768 |
RW |
Resolution mode 1024x768 pixel |
3 |
1280x720 |
RW |
Resolution mode 1280x720 pixel |
4 |
1920x1080 |
RW |
Resolution mode 1920x1080 pixel |
5 |
3x2 |
RW |
Resolution mode 3x2 pixel |
6 |
80x60 |
RW |
Resolution mode 80x60 pixel |
7 |
320x240 |
RW |
Resolution mode 320x240 pixel |
RESOLUTION_MODE_SUPPORT Register
Register offset: 0x00C Read only. Stores supported resolution modes as a bitmask. Each bit represents a supported mode (1 = supported, 0 = unsupported). Before requesting to set a certain resolution mode, check, if it is supported. E.g. if mode 0x10 is requested while only modes 0x0 (640x480) and 0x1 (800x600) are supported, an error will occur.
COLOR_MODE Register
Register offset: 0x010 Set register bit to choose respective color mode. For undefined color modes, it is the implementation’s responsibility to select appropriate entries from the COLOR_MAP. COLOR_MAP: Address range 0x100 - 0x4FF contains 256 colors, with 4 bytes per entry.
Bit |
Field name |
Attr. |
Description |
|---|---|---|---|
0 |
1 Bit Monochrome |
RW |
Black and White only |
1 |
2-Bit Mapped |
RW |
4 colors from the COLOR_MAP |
2 |
2-Bit Grayscale |
RW |
4 levels of gray from the COLOR_MAP |
3 |
3-Bit Mapped |
RW |
8 colors from the COLOR_MAP |
4 |
3-Bit RGB |
RW |
1 bit per RGB channel: black, red, green, blue, cyan, magenta, yellow and white |
5 |
3-Bit Grayscale |
RW |
8 levels of gray from the COLOR_MAP |
6 |
4-Bit Mapped |
RW |
16 colors from the COLOR_MAP |
7 |
4-Bit RGBI |
RW |
4-bit RGBI is similar to the 3-bit RGB but adds one bit for dark/bright intensity, see CGA-standard |
8 |
4-Bit Grayscale |
RW |
16 levels of gray from the COLOR_MAP |
9 |
8-Bit Mapped |
RW |
All 256 colors from COLOR_MAP |
10 |
8-Bit RGB332 |
RW |
3 bits red, 3 bits green, 2 bits blue |
11 |
8-Bit Grayscale |
RW |
256 levels of gray: RGB(0, 0, 0) to RGB (255, 255 , 255) |
12 |
16-Bit RGB565 |
RW |
RGB565 Color mode with 16 bits per pixel: 5 bits red, 6 bits green, 5 bits blue. |
13 |
32-Bit RGB888 |
RW |
Color mode with 32 bits per pixel, uses 24-bit truecolor (RGB888) packed into a 32-bit format for memory alignment. |
COLOR_MODE_SUPPORT Register
Register offset: 0x014 Read only. Stores supported color modes as a bitmask. Each bit represents a supported mode (1 = supported, 0 = unsupported). Before requesting to set a certain color mode, check, if it is supported. E.g. when trying to set resolution mode 0x10, but only 0x0 (640x480) and 0x1(800x600) is defined, an error will occur.
SCANLINE Register
Register offset: 0x018 Read only. Provides the current scanline index being rendered.
COLOR_MAP Register
Register offset: 0x024 Read only. The COLOR_MAP register range (0x100 - 0x4FF) consists of 256 registers for color values. Each entry is a 32-bit value representing a color, with the bits allocated for the RGB color channels. The layout is as follows:
0xUURRGGBB |
Description |
|---|---|
UU |
Unused |
RR |
Red |
GG |
Green |
BB |
Blue |
These registers are optional and depend on the supported color modes of the hardware. They are necessary, when using a mapped color mode. Each entry in the COLOR_MAP is structured as a 32-bit value, where the bits are allocated for the RGB color channels. Specifically, the layout is as follows:
Example use:
Hex (ARGB) |
RGB Decimal |
Color |
|---|---|---|
0x00FF0000 |
(255, 0, 0) |
Red |
0x0000FF00 |
(0, 255, 0) |
Green |
0x000000FF |
(0, 0, 255) |
Blue |
Practical Access
The COLOR_MAP is used by setting the corresponding color index in color modes that map directly to the color table. For example, in a 2-bit color mode, you could access the first four colors with COLOR_MAP[index] as follows:
0 = Black(0x00000000)1 = White(0x00FFFFFF)2 = Red(0x00FF0000)3 = Green(0x0000FF00)
In higher bit modes (e.g., 8-bit), you can access up to 256 colors from the COLOR_MAP. A specific color is referenced by its index, and the respective color value is retrieved from the corresponding COLOR_MAP entry.
FRAMEBUFFER Register
Register offset: 0x424 Pointer to framebuffer memory in the piconut system.
Color Palettes
In color_palettes.h, several predefined color palettes are provided for different color modes. These palettes can be used to initialize the COLOR_MAP register in the video peripheral. Here is some documentation for the available color palettes:
Default color palette definitions.
These palettes are hardcoded color arrays used by the video peripheral. They provide a set of predefined colors for different color modes. If needed exchange these palettes with custom ones e.g. from https://lospec.com/palette-list Later a selection could be implemented for the palettes. These arrays account for ~1300 LUTs
Variables
-
const uint32_t palette_2_bit[4]
2-Bit Color Palette
By default a 4-color palette featuring cosmic magentas and yellows. Source: https://lospec.com/palette-list/galactic-pizza
-
const uint32_t palette_3_bit[8]
3-Bit Color Palette
By default a balanced 8-color palette with cute cotton candy colors. Source: https://lospec.com/palette-list/pollen8
-
const uint32_t palette_4_bit[16]
4-Bit Color Palette
By default a versatile 16-color palette including a wide range of hues and neutrals. Source: https://lospec.com/palette-list/go-line
-
const uint32_t palette_8_bit[256]
Standard 8-Bit RGB Palette.
By default color palette “Doctor Robotnik’s Ring Racers Palette Palette” Source: https://lospec.com/palette-list/doctor-robotniks-ring-racers-palette
Video Driver
There is a video driver available to interface with the video peripheral from software.
Usage
For using the video modules in your Piconut project, look at the reference implementation in sw/apps/video_spectrum/video_spectrum.c.
Here is a brief overview of the steps needed to set up and use the video modules:
Build a PicoNut-System with a video modules (soft or hardware).
Use the video_driver to initialize the video module.
video_t video;
// Initialize the driver instance
if(video_init(&video, 0, RESOLUTION_MODE_640x480, COLOR_MODE_1_MONO) != VIDEO_OK)
{
printf("Error: Could not initialize video driver.\n");
return -1;
}
Use the video driver functions to manipulate the framebuffer and control the video output.
video_write_pixel(&video, x, y, color_index);
Reference
Enums
Functions
-
video_res_t video_init(video_t *self, uintptr_t base_addr, e_resolution_mode res_mode, e_color_mode color_mode)
Initializes the video module, sets modes, and caches dimensions.
- Parameters:
self – Pointer to driver instance.
base_addr – Optional physical base address of the hardware registers. If 0, default address is used.
res_mode – Initial resolution mode.
color_mode – Initial color mode.
- Returns:
VIDEO_OK on success.
-
void video_set_enable(video_t *self, bool enable)
Enables or disables the video output.
- Parameters:
self – Pointer to driver instance.
enable – True to enable, false to disable.
-
bool video_get_enabled(video_t *self)
Reads the current enable state from VIDEO_REG_CONTROL.
- Parameters:
self – Pointer to driver instance.
- Returns:
True if enabled.
-
void video_set_interrupts(video_t *self, bool enable)
Enables or disables video interrupts.
- Parameters:
self – Pointer to driver instance.
enable – True to enable interrupts, false to disable.
-
bool video_get_interrupts(video_t *self)
Reads the current interrupt enable state from VIDEO_REG_INTERRUPT.
- Parameters:
self – Pointer to driver instance.
- Returns:
True if interrupts are enabled.
-
uint32_t video_get_status(video_t *self)
Reads the hardware status register.
- Parameters:
self – Pointer to driver instance.
- Returns:
32-bit status register value.
-
video_res_t video_set_mode(video_t *self, e_resolution_mode res_mode, e_color_mode color_mode)
Sets resolution and color mode if supported by the hardware.
Note
Updates internal width/height cache upon success.
-
void video_update_modes(video_t *self)
Force update of the cached resolution and color mode from hardware.
-
uint32_t video_get_current_scanline(video_t *self)
Reads the currently rendering scanline.
- Parameters:
self – Pointer to driver instance.
- Returns:
Current scanline index.
-
uint32_t video_get_supported_res_modes(video_t *self)
Returns the bitmask of supported resolution modes.
-
uint32_t video_get_supported_color_modes(video_t *self)
Returns the bitmask of supported color modes.
-
uint32_t video_read_color_map(video_t *self, uint8_t index)
Reads a 32-bit color value from the hardware color map.
- Parameters:
self – Pointer to driver instance.
index – Color index (0-255).
- Returns:
32-bit color value.
-
volatile uint32_t *video_get_framebuffer(video_t *self)
Returns a volatile pointer to the start of the framebuffer memory.
-
video_res_t video_write_pixel(video_t *self, uint32_t x, uint32_t y, uint32_t color)
Safe pixel write with bounds checking.
- Parameters:
self – Pointer to driver instance.
x – X-coordinate.
y – Y-coordinate.
color – Color value to write.
- Returns:
VIDEO_OK if within bounds, VIDEO_ERR_INVALID otherwise.
-
struct video_t
- #include <video_driver.h>
Video Driver Instance Structure.
Public Members
-
uintptr_t base_addr
Readonly base address of the video module registers
-
uint32_t fb_width
Readonly framebuffer width from hardware
-
uint32_t fb_height
Readonly framebuffer height from hardware
-
e_resolution_mode res_mode
Readonly resolution mode from hardware
-
e_color_mode color_mode
Readonly color mode from hardware
-
uintptr_t base_addr
Soft Graphics
- group c_soft_graphics
This module is used to simulate the graphics peripheral. The module is implemented as a soft peripheral and can be used in the simulation environment.
- Author
Konrad Armbrecht
The module has a register for storing the output-images, called framebuffer. The framebuffer is set up as a single large array where all output-image lines are stored successively. The size is calculated by width and height given from the constructor. With a 10 × 10 px resolution, you have 100 array elements, and the framebuffer[10] element is the first pixel of the second output-image line. The module has a 32-bit memory interface and can be accessed by the soft peripheral interface.
Note: The module is not synthesizable and is only used for simulation purposes.
Registers
-
struct regs_t
Registers of the c_soft_graphics module.
-
enum c_soft_graphics::e_regs
Register address offsets.
Values:
-
enumerator CONTROL = 0x0
-
enumerator WIDTH = 0x4
-
enumerator HEIGHT = 0x8
-
enumerator RESOLUTION_MODE = 0xC
-
enumerator RESOLUTION_MODE_SUPPORT = 0x10
-
enumerator COLOR_MODE = 0x14
-
enumerator COLOR_MODE_SUPPORT = 0x18
-
enumerator FRAMEBUFFER = 0x1C
-
enumerator CONTROL = 0x0
Functions
-
c_soft_graphics::c_soft_graphics(uint64_t base_address, ResolutionMode resolution_mode, ColorMode color_mode, uint32_t clock_frequency, void (*flush_func)(uint32_t *framebuffer, uint32_t framebuffer_size_in_px), std::function<void(bool)> callback_signal_graphics_interrupt = nullptr)
Constructor.
- Parameters:
base_address – base address of the peripheral in the address space of the simulation
resolution_mode – resolution mode (recommended for performance reasons: “1” -> 60x80 px)
color_mode – color depth in bits per pixel (here only “4” is implemented -> 32 bit)
clock_frequency – maximum value for interrupt counter
flush_func – Callback function to flush the framebuffer to GUI.
callback_signal_graphics_interrupt – Callback function to signal graphics interrupts.
-
c_soft_graphics::~c_soft_graphics()
Destructor.
-
void c_soft_graphics::flush_graphics(void (*flush_to_gui)(uint32_t*, uint32_t), uint32_t framebuffer_size)
Flushes the graphics framebuffer using the provided callback function.
- Parameters:
flush_to_gui – Callback function to send framebuffer data to the GUI.
framebuffer_size – Size of the framebuffer in pixels.
-
void c_soft_graphics::register_meip_callback(std::function<void(bool)> callback)
Register a callback function for graphics interrupt changes.
- Parameters:
callback – The callback function
-
void c_soft_graphics::on_rising_edge_clock() override
Triggers interrupts when max_counter value is reached. This function is automatically called on every rising edge of the clock.
-
void c_soft_graphics::clear_framebuffer()
Global config
Graphics-related settings in global config file /hw/piconut/config.mk
CFG_GRAPHICS_BASE_ADDRESS = 0x90000000
Defines the memory address of the graphics peripheral in the simulator address space.
Hardware Video
The m_video peripheral offers an interface to external monitors. Currently, output via a VGA compatible interface at an resolution of 640x480 is supported. A wishbone interface gives access to control and status registers, as well as access to the internal framebuffer. Following figure shows the structure of this peripheral. It is designed around a streaming pipeline. Video signals flow unidirectional from source (e.g. internal framebuffer ) to sink modules (e.g. VGA signal generator). Controlled by the line/column counter, it outputs a continuous stream of pixels.
Fig. 25 Structure of the m_video peripheral.
Color Modes
This peripheral supports several color modes shown in the table below. The color translator module interprets colors according to this table.
Fig. 26 Table of color mode definitions.
The 4 bit RGBI mode is similar to the 3 bit RGB mode, with an additional bit to select between two color intensities. Its 16 colors are shown in the figure below.
Fig. 27 4 bit RGBI color palette
Unimplemented Features
Currently, only a resolution of 640x480 is supported. Other resolution need different video-clock. We use the only resolution possible with 25MHz pixel clock. (although 640x480 is defined with 25.175 MHz it works)
Interrupts are not implemented yet.
Framebuffer memory is limited.
Scaling for multiple resolutions is not implemented yet.
Statuses other than “ready”.
Configuration
- group m_video_config
Configuration macros for the Video peripheral module.
Modules
Framebuffer
-
SC_MODULE(m_framebuffer_source)
Video source module with internal framebuffer.
The framebuffer source generates a video signal from an internal framebuffer. It offers two separate interfaces for interaction with the system and video output, each driven by their own clocks.
- Author
Martin Erichsen
The system-side port offers a read/write interface to the internal framebuffer.
From the video-side port,
vid_column_inandvid_lineare used to address pixels inside the buffer. Calculation of the buffer address is given by following equation:This results in a fixed upscaling factor of 16 and a maximum column address of 639. Column addresses greater than 639 wrap over to the next line. Whileaddress = (column / PN_CFG_VIDEO_FB_SCALING_DIVISOR) + (line / PN_CFG_VIDEO_FB_SCALING_DIVISOR) * (RESOLUTION_X / PN_CFG_VIDEO_FB_SCALING_DIVISOR)
vid_enable_inis disabled, zero will be output overvid_output.Generation of the video signal requires one clock cycle.
- Ports:
- Parameters:
sys_clk – [in] system-side clock input
reset – [in] system-side reset
ctl_addr_in – [in] system-side buffer address
ctl_data_in – [in] system-side buffer write data
ctl_write_en_in – [in] system-side buffer write enable
vid_clk – [in] video-side clock input
vid_enable_in – [in] video-side output enable
vid_column_in – [in] video-side horizontal address
vid_line_in – [in] video-side vertical address
vid_data_out – [out] video-side video output
-
SC_MODULE(m_framebuffer_ram)
Dual port RAM block for use as internal framebuffer.
The framebuffer RAM is a dual port RAM based on the Dual Port Block RAM template.
- Author
Martin Erichsen
Each port provides an interface for reading and writing into the memory. To read from the memory, an address must be set and enable must be activated. Additionally, for write access, input data must be provided and write enable must be set.
Its size can be changed using the configuration variable
CFG_WB_GRAPHICS_FB_SIZE.When synthesizing, this module is replaced by a verilog implementation held in a separate file.
- Ports:
- Parameters:
clka – [in] clock input for port A
wea – [in] write enable for port A
ena – [in] enable bram for port A
addra – [in] address for port A
dia – [in] data in for port A
doa – [out] data out for port A
clkb – [in] clock input for port B
web – [in] write enable for port B
enb – [in] enable bram for port B
addrb – [in] address for port B
dib – [in] data in for port B
dob – [out] data out for port B
Color Translator
-
SC_MODULE(m_color_translator)
The module translates an input color to 24-bit RGB. color_out[23:16], color_out[15:8], color_out[7:0] correspond to the 8-bit wide red, green and blue components respectively.
color_modeselects the input color’s format.- Author
Martin Erichsen
Conversion requires one clock cycle.
Color map modes are currently not supported and output black (0x000000).
- Ports:
- Parameters:
clk – [in] clock of the module
reset – [in] reset of the module
color_mode_in – [in] color mode of the input color
color_in – [in] input color
color_out – [out] output color as 24-bit RGB
VGA Signal Generator
-
SC_MODULE(m_vga_color_generator)
Generator for VGA compatible 4-bit wide color.
Generates 4-bit wide red, green and blue signals from a 24-bit RGB signal for with 4-bit video DACs.
blank_enableforces the output to black (all three channels to 0).- Authors
Beaurel I. Ngaleu
- Author
Martin Erichsen
Requires one clock cycle to output.
- Ports:
- Parameters:
clk – [in] clock of the module
reset – [in] reset of the module
blank_enable_in – [in] enable output blanking
video_in – [in] input color
vga_red_out – [out] 4-bit red channel intensity
vga_green_out – [out] 4-bit green channel intensity
vga_blue_out – [out] 4-bit blue channel intensity
-
SC_MODULE(m_vga_sync_generator)
Generator for VGA compatible hsync and vsync signals.
Outputs horizontal and vertical sync signals. Both hsync and vsync are inverted, so they are at logic 0 when asserted during synchronization and logic 1 otherwise.
- Author
Martin Erichsen
Either sync signal is asserted or deasserted the next clock cycle their address value matches
vga_*_beginorvga_*_end. They do not change otherwise.While
enableis not set, both sync signals are forced to deasserted state on the next clock cycle.- Ports:
- Parameters:
clk – [in] clock of the module
reset – [in] reset of the module
vga_hsync_begin_out – [in] horizontal address to assert hsync after
vga_hsync_end_out – [in] horizontal address to deassert hsync after
vga_vsync_begin_out – [in] vertical address to assert vsync after
vga_vsync_end_out – [in] vertical address to deassert vsync after
enable_in – [in] synchronous enable
column_in – [in] current column counter address
line_in – [in] current line counter address
vga_hsync_out – [out] horizontal synchronization signal
vga_vsync_out – [out] vertical synchronization signal
VGA Timing Table
-
SC_MODULE(m_vga_timings)
Video timing configuration lookup table.
Outputs a timing configuration selected by
resolution_mode. Currently, only a resolution of 640x480 at pixel clock of 25 MHz is supported.- Author
Martin Erichsen
This module has no clock, lookup is done combinatorically.
- Ports:
- Parameters:
resolution_mode – [in] active resolution mode
column_active – [out] number of active pixels on a video line
column_end – [out] total number of pixels on a video line
line_active – [out] number of active lines in a video frame
line_end – [out] total number of pixels in a video frame
vga_hsync_begin – [out] horizontal address to assert hsync after
vga_hsync_end – [out] horizontal address to deassert hsync after
vga_vsync_begin – [out] vertical address to assert vsync after
vga_vsync_end – [out] vertical address to deassert vsync after
Wishbone Slave Interface
-
SC_MODULE(m_video_wb)
Video Module Wishbone Interface.
It translates Wishbone bus accesses (adr/dat/we/stb/cyc/sel) into:
Updates of video configuration registers (Control, Resolution, Color),
Direct access to the video Framebuffer RAM,
Status readback for current video scanlines and module state.
Offers data from register to other video modules
Wishbone Ports (adr/dat/we/stb/cyc/sel) are included in pn_wishbone_slave_t wb_slave
- Connection to Video/Graphics submodules:
- Parameters:
clk – [in] : System clock.
reset – [in] : Active-high reset.
vid_line_in – [in] : Current horizontal/vertical scanline being processed.
fb_ctl_data_out_in – [in] : Data read from the Framebuffer RAM.
fb_ctl_addr_out – [out] : Address bus for the Framebuffer RAM.
fb_ctl_data_in_out – [out] : Data bus to be written to Framebuffer RAM.
fb_ctl_write_en_out – [out] : Write enable signal for Framebuffer RAM.
enable_video_out – [out] : Global video output enable signal.
enable_interrupt_out – [out] : Video interrupt enable signal.
resolution_mode_out – [out] : Selected resolution configuration.
color_mode_out – [out] : Selected color depth/format configuration.
m_video
-
SC_MODULE(m_video)
This module connects several subsystems such as Wishbone control, framebuffer, VGA sync generator, color generator, and timing logic into a complete video system.
- Ports:
- Parameters:
clk – [in] Main clock signal
reset – [in] Global reset signal
vga_red_out – [out] 4-bit VGA red color output
vga_green_out – [out] 4-bit VGA green color output
vga_blue_out – [out] 4-bit VGA blue color output
vga_hsync_out – [out] VGA horizontal sync output
vga_vsync_out – [out] VGA vertical sync output