# HG changeset patch # User Paul Boddie # Date 1541179566 -3600 # Node ID caa06f34e4cc2e1b7ce194f3868feb76092a6a2b # Parent f31682a2289e1beaa671f02c211ef72c68588257 Consolidated the CPU- and DMA-based display functionality. diff -r f31682a2289e -r caa06f34e4cc examples/vga-cpu/Makefile --- a/examples/vga-cpu/Makefile Fri Nov 02 02:15:09 2018 +0100 +++ b/examples/vga-cpu/Makefile Fri Nov 02 18:26:06 2018 +0100 @@ -27,8 +27,8 @@ # Ordering of objects is important and cannot be left to replacement rules. -SRC = $(START_SRC) main.c $(COMMON_SRC) $(DISPLAY_CPU_SRC) screendata.S sprite.S font.S -OBJ = $(START_OBJ) main.o $(COMMON_OBJ) $(DISPLAY_CPU_OBJ) screendata.o sprite.o font.o +SRC = $(START_SRC) main.c $(COMMON_SRC) $(DISPLAY_SRC) screendata.S sprite.S font.S +OBJ = $(START_OBJ) main.o $(COMMON_OBJ) $(DISPLAY_OBJ) screendata.o sprite.o font.o # Application-specific adjustments. # See: examples/vga/Makefile diff -r f31682a2289e -r caa06f34e4cc examples/vga-cpu/README.txt --- a/examples/vga-cpu/README.txt Fri Nov 02 02:15:09 2018 +0100 +++ b/examples/vga-cpu/README.txt Fri Nov 02 18:26:06 2018 +0100 @@ -25,7 +25,8 @@ access to the RAM. Even if, in such situations, the CPU may be able to access flash memory to load instructions, programs typically end up accessing RAM at some point, and this would effectively limit the concurrency within the -system. +system. Certainly, this approach seems to result in slower programs than the +plain DMA-based approach. One potential advantage of this approach is in the flexibility that might be achieved by manipulating the pixel data. With DMA, data is transferred as it diff -r f31682a2289e -r caa06f34e4cc examples/vga/main.c --- a/examples/vga/main.c Fri Nov 02 02:15:09 2018 +0100 +++ b/examples/vga/main.c Fri Nov 02 18:26:06 2018 +0100 @@ -30,21 +30,33 @@ #include "font.h" #include "main.h" #include "vga.h" +#include "vga_display.h" -#ifndef TRANSFER_CPU -#include "vga_display.h" + + +/* Define DMA channels if not indicated in the build configuration. */ + +/* CPU-based transfers: no channels. */ + +#ifdef TRANSFER_CPU +#define LINE_CHANNELS 0 +#define SCROLL_XSTEP 1 + +/* DMA-based transfers: single channel by default. */ + #else -#include "vga_display_cpu.h" + +#ifndef LINE_CHANNELS +#define LINE_CHANNELS 1 +#endif + +#define SCROLL_XSTEP LINE_CHANNELS #endif /* Define timers if not indicated in the build configuration. */ -#ifndef LINE_CHANNELS -#define LINE_CHANNELS 1 -#endif - #ifndef LINE_TIMER #define LINE_TIMER 2 #endif @@ -116,10 +128,25 @@ static void animate(uint32_t delay) { + /* Stored region behind the sprite. */ + uint8_t background[sprite_width * sprite_height]; + + /* Sprite position. */ + int x, y; - int dir[] = {1, 0, -1, 0, 1}, i = 0, width, column_width; - int xsource, xdisplay, xorigin = 0, yorigin = 0; + + /* Scrolling directions. */ + + int dir[] = {1, 0, -1, 0, 1}, i = 0; + + /* Scrolling position. */ + + int xorigin = 0, yorigin = 0; + + /* Replotted column details. */ + + int width, column_width, xsource, xdisplay; while (1) { @@ -171,7 +198,7 @@ involves two pixel increments and thus requires a two- pixel column to be plotted. */ - width = dir[i] * LINE_CHANNELS; + width = dir[i] * SCROLL_XSTEP; column_width = width < 0 ? -width : width; /* Plot either at the left or right edge. */ @@ -283,11 +310,7 @@ timer and any transfer timer, with an initiating channel being introduced if a transfer timer is specified. */ -#ifndef TRANSFER_CPU init_vga_with_timers(&display_config, LINE_CHANNELS, LINE_TIMER, TRANSFER_TIMER); -#else - init_vga_with_timer(&display_config, LINE_TIMER); -#endif /* Configure VGA output transfer to the output register, also configuring output compare units for horizontal and vertical sync. */ @@ -326,29 +349,7 @@ void interrupt_handler(void) { - uint32_t ifs; - -#ifdef TRANSFER_CPU - /* Check for a timer interrupt condition. */ - - ifs = REG(TIMERIFS) & TIMER_INT_FLAGS(LINE_TIMER, TxIF); - - if (ifs) - { - vga_transfer_interrupt_handler(); - CLR_REG(TIMERIFS, ifs); - } -#endif - - /* Check for a OC1 interrupt condition. */ - - ifs = REG(OCIFS) & OC_INT_FLAGS(1, OCxIF); - - if (ifs) - { - vga_interrupt_handler(); - CLR_REG(OCIFS, ifs); - } + vga_interrupt_handler(); } diff -r f31682a2289e -r caa06f34e4cc include/vga_display.h --- a/include/vga_display.h Fri Nov 02 02:15:09 2018 +0100 +++ b/include/vga_display.h Fri Nov 02 18:26:06 2018 +0100 @@ -33,9 +33,14 @@ void (*state_handler)(); + /* Common transfer properties. */ + + int line_timer; + uint32_t output; + /* DMA transfer properties. */ - int line_timer, line_channels, transfer_int_num; + int line_channels, transfer_int_num; /* Horizontal and vertical sync peripherals. */ @@ -69,6 +74,8 @@ void vga_configure_transfer(uint32_t output); +void vga_configure_dma_transfer(uint32_t output); + /* Initialisation helpers. */ void vga_configure_line_channel(int channel, int int_num, enum dma_chain chain, @@ -80,6 +87,8 @@ /* Interrupt handlers. */ void vga_interrupt_handler(void); +void vga_hsync_interrupt_handler(void); +void vga_transfer_interrupt_handler(void); /* Display state handlers. */ @@ -88,7 +97,7 @@ void vfp_active(void); void vsync_active(void); -/* Display operations. */ +/* DMA transfer operations. */ void start_visible(void); void update_visible(void); diff -r f31682a2289e -r caa06f34e4cc include/vga_display_cpu.h --- a/include/vga_display_cpu.h Fri Nov 02 02:15:09 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,95 +0,0 @@ -/* - * VGA display-related functions. - * - * Copyright (C) 2018 Paul Boddie - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __VGA_DISPLAY_CPU_H__ -#define __VGA_DISPLAY_CPU_H__ - -#include "pic32_c.h" -#include "display.h" - - - -/* Display configuration type. */ - -typedef struct -{ - /* Current display state handler. */ - - void (*state_handler)(); - - /* Transfer properties. */ - - int line_timer; - uint32_t output; - - /* Horizontal and vertical sync peripherals. */ - - int hsync_unit, vsync_unit; - - /* Current scanline. */ - - uint32_t line; - - /* Pointers to pixel lines. */ - - uint8_t *linedata; - - /* General display configuration. */ - - display_config_t *display_config; - -} vga_display_t; - - - -/* Initialisation. */ - -void init_vga(display_config_t *display_config, int line_timer); - -void init_vga_with_timer(display_config_t *display_config, int line_timer); - -void vga_configure_sync(int hsync_unit, int vsync_unit); - -void vga_configure_transfer(uint32_t output); - -/* Interrupt handlers. */ - -void vga_interrupt_handler(void); -void vga_transfer_interrupt_handler(void); - -/* Display state handlers. */ - -void vbp_active(void); -void visible_active(void); -void vfp_active(void); -void vsync_active(void); - -/* Display operations. */ - -void start_visible(void); -void update_visible(void); -void stop_visible(void); -void update_transfers(int enable); - -/* Vertical sync operations. */ - -void vsync_high(void); -void vsync_low(void); - -#endif /* __VGA_DISPLAY_CPU_H__ */ diff -r f31682a2289e -r caa06f34e4cc lib/vga_display.c --- a/lib/vga_display.c Fri Nov 02 02:15:09 2018 +0100 +++ b/lib/vga_display.c Fri Nov 02 18:26:06 2018 +0100 @@ -76,11 +76,18 @@ /* The timers have no prescaling (0). */ timer_init(line_timer, 0, display_config->hfreq_limit); + + /* Enable interrupt requests when the CPU needs to perform the transfer, as + opposed to the DMA channels doing so. */ + + if (!line_channels) + timer_init_interrupt(line_timer, 7, 3); + timer_on(line_timer); /* Configure a separate transfer timer, if indicated. */ - if (!transfer_timer || (transfer_timer == line_timer)) + if (!line_channels || !transfer_timer || (transfer_timer == line_timer)) return; /* The timer wraps around immediately. */ @@ -91,9 +98,19 @@ +/* Configure the transfer of pixel data. */ + +void vga_configure_transfer(uint32_t output) +{ + vga_display.output = output; + + if (vga_display.line_channels) + vga_configure_dma_transfer(output); +} + /* Configure DMA channels for the transfer of pixel data. */ -void vga_configure_transfer(uint32_t output) +void vga_configure_dma_transfer(uint32_t output) { int dual_channel = vga_display.line_channels == 2; int channel = 0; @@ -226,14 +243,73 @@ -/* Interrupt handlers. */ +/* Display state machine interrupt handler. */ void vga_interrupt_handler(void) { + uint32_t ifs; + + if (!vga_display.line_channels) + { + /* Check for a timer interrupt condition. */ + + ifs = REG(TIMERIFS) & TIMER_INT_FLAGS(vga_display.line_timer, TxIF); + + if (ifs) + { + vga_transfer_interrupt_handler(); + CLR_REG(TIMERIFS, ifs); + } + } + + /* Check for a OC1 interrupt condition. */ + + ifs = REG(OCIFS) & OC_INT_FLAGS(1, OCxIF); + + if (ifs) + { + vga_hsync_interrupt_handler(); + CLR_REG(OCIFS, ifs); + } + +} + +/* Display state machine interrupt handler. */ + +void vga_hsync_interrupt_handler(void) +{ vga_display.line += 1; vga_display.state_handler(); } +/* Visible region pixel output handler, used when the CPU is responsible for + producing pixel output rather than the DMA channels. */ + +void vga_transfer_interrupt_handler(void) +{ + display_config_t *cfg = vga_display.display_config; + uint8_t *current, *end, *output; + + if (vga_display.state_handler != visible_active) + return; + + /* Generate the pixel signal. */ + + output = (uint8_t *) vga_display.output; + end = vga_display.linedata + cfg->line_length; + + /* This is potentially not as efficient as loading words and shifting bytes + but it appears difficult to implement that approach without experiencing + data load exceptions. */ + + for (current = vga_display.linedata; current < end; current++) + REG(output) = *current; + + /* Reset the signal level. */ + + REG(output) = 0; +} + /* Vertical back porch region. */ @@ -250,7 +326,9 @@ /* Set the line address. */ vga_display.linedata = vga_display.display_config->screen_start; - start_visible(); + + if (vga_display.line_channels) + start_visible(); } /* Visible region. */ @@ -271,7 +349,8 @@ vga_display.linedata -= cfg->screen_size; } - update_visible(); + if (vga_display.line_channels) + update_visible(); return; } @@ -281,7 +360,8 @@ /* Disable the channel for the next line. */ - stop_visible(); + if (vga_display.line_channels) + stop_visible(); } /* Vertical front porch region. */ diff -r f31682a2289e -r caa06f34e4cc lib/vga_display_cpu.c --- a/lib/vga_display_cpu.c Fri Nov 02 02:15:09 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,242 +0,0 @@ -/* - * Generate a VGA signal using a PIC32 microcontroller. - * - * Copyright (C) 2017, 2018 Paul Boddie - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "pic32_c.h" -#include "init.h" -#include "vga_display_cpu.h" - - - -/* Display state. */ - -vga_display_t vga_display; - - - -/* Initialise the state machine. */ - -void init_vga(display_config_t *display_config, int line_timer) -{ - /* Display parameters. */ - - vga_display.display_config = display_config; - - /* Initial state. */ - - vga_display.state_handler = vbp_active; - vga_display.line = 0; - - /* Configure a general display timer to start line data transfer and for the - horizontal sync. */ - - vga_display.line_timer = line_timer; -} - -/* Initialise a separate transfer timer if different from the general display - timer. */ - -void init_vga_with_timer(display_config_t *display_config, int line_timer) -{ - /* Initialise the basic properties of the display. */ - - init_vga(display_config, line_timer); - - /* Configure a line timer for horizontal sync and line data transfers. The - interrupt is handled to produce line data and to drive the display state - machine. */ - - /* The timer has no prescaling (0). */ - - timer_init(line_timer, 0, display_config->hfreq_limit); - timer_init_interrupt(line_timer, 7, 3); - timer_on(line_timer); -} - - - -/* Configure the output port. */ - -void vga_configure_transfer(uint32_t output) -{ - vga_display.output = output; -} - -/* Configure output compare units for horizontal and vertical sync. */ - -void vga_configure_sync(int hsync_unit, int vsync_unit) -{ - /* Record the peripherals in use. */ - - vga_display.hsync_unit = hsync_unit; - vga_display.vsync_unit = vsync_unit; - - /* Horizontal sync. */ - - /* Configure output compare in dual compare (continuous output) mode using - the timer as time base. */ - - oc_init(hsync_unit, 0b101, vga_display.line_timer); - oc_set_pulse(hsync_unit, vga_display.display_config->hsync_end); - oc_set_pulse_end(hsync_unit, vga_display.display_config->hsync_start); - oc_init_interrupt(hsync_unit, 7, 3); - oc_on(hsync_unit); - - /* Vertical sync. */ - - /* Configure output compare in single compare (output driven low) mode using - the timer as time base. The unit is enabled later. It is only really used - to achieve precisely-timed level transitions in hardware. */ - - oc_init(vsync_unit, 0b010, vga_display.line_timer); - oc_set_pulse(vsync_unit, 0); -} - - - -/* Interrupt handlers. */ - -void vga_interrupt_handler(void) -{ - vga_display.line += 1; - vga_display.state_handler(); -} - -/* Visible region pixel output handler. */ - -void vga_transfer_interrupt_handler(void) -{ - display_config_t *cfg = vga_display.display_config; - uint8_t *current, *end, *output; - - if (vga_display.state_handler != visible_active) - return; - - /* Generate the pixel signal. */ - - output = (uint8_t *) vga_display.output; - end = vga_display.linedata + cfg->line_length; - - /* This is potentially not as efficient as loading words and shifting bytes - but it appears difficult to implement that approach without experiencing - data load exceptions. */ - - for (current = vga_display.linedata; current < end; current++) - REG(output) = *current; - - /* Reset the signal level. */ - - REG(output) = 0; -} - - - -/* Vertical back porch region. */ - -void vbp_active(void) -{ - if (vga_display.line < vga_display.display_config->visible_start) - return; - - /* Enter the visible region. */ - - vga_display.state_handler = visible_active; - - /* Set the line address. */ - - vga_display.linedata = vga_display.display_config->screen_start; -} - -/* Visible region. */ - -void visible_active(void) -{ - display_config_t *cfg = vga_display.display_config; - - if (vga_display.line < cfg->vfp_start) - { - /* Update the line address and handle wraparound. */ - - if (!(vga_display.line % cfg->line_multiplier)) - { - vga_display.linedata += cfg->line_length; - - if (vga_display.linedata >= cfg->screen_limit) - vga_display.linedata -= cfg->screen_size; - } - - return; - } - - /* End the visible region. */ - - vga_display.state_handler = vfp_active; -} - -/* Vertical front porch region. */ - -void vfp_active(void) -{ - if (vga_display.line < vga_display.display_config->vsync_start) - return; - - /* Enter the vertical sync region. */ - - vga_display.state_handler = vsync_active; - - /* Bring vsync low when the next line starts. */ - - vsync_low(); -} - -/* Vertical sync region. */ - -void vsync_active(void) -{ - if (vga_display.line < vga_display.display_config->vsync_end) - return; - - /* Start again at the top of the display. */ - - vga_display.line = 0; - vga_display.state_handler = vbp_active; - - /* Bring vsync high when the next line starts. */ - - vsync_high(); -} - - - -/* Bring vsync low (single compare, output driven low) when the next line - starts. */ - -void vsync_low(void) -{ - oc_init(vga_display.vsync_unit, 0b010, vga_display.line_timer); - oc_on(vga_display.vsync_unit); -} - -/* Bring vsync high (single compare, output driven high) when the next line - starts. */ - -void vsync_high(void) -{ - oc_init(vga_display.vsync_unit, 0b001, vga_display.line_timer); - oc_on(vga_display.vsync_unit); -} diff -r f31682a2289e -r caa06f34e4cc mk/common.mk --- a/mk/common.mk Fri Nov 02 02:15:09 2018 +0100 +++ b/mk/common.mk Fri Nov 02 18:26:06 2018 +0100 @@ -57,9 +57,6 @@ DISPLAY_SRC = $(DISPLAY_COMMON_SRC) $(LIBDIR)/vga_display.c DISPLAY_OBJ = $(DISPLAY_COMMON_OBJ) $(LIBDIR)/vga_display.o -DISPLAY_CPU_SRC = $(DISPLAY_COMMON_SRC) $(LIBDIR)/vga_display_cpu.c -DISPLAY_CPU_OBJ = $(DISPLAY_COMMON_OBJ) $(LIBDIR)/vga_display_cpu.o - # Common linker script. SCRIPT = $(LIBDIR)/payload.ld