# HG changeset patch # User Paul Boddie # Date 1494086036 -7200 # Node ID 71d6504cb824e454a47d5500fee9ca2c9e3f45f1 An attempt to produce VGA output signals with a PIC32 device. diff -r 000000000000 -r 71d6504cb824 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Sat May 06 17:53:56 2017 +0200 @@ -0,0 +1,75 @@ +# Makefile - Build the PIC32 VGA payload +# +# Copyright (C) 2015, 2017 Paul Boddie +# Copyright (C) Xiangfu Liu +# +# 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 . + +ARCH = mipsel-linux-gnu +CC = $(ARCH)-gcc +LD = $(ARCH)-ld +NM = $(ARCH)-nm +OBJCOPY=$(ARCH)-objcopy +OBJDUMP=$(ARCH)-objdump + +# NOTE: -O2 is actually needed to prevent memcpy references, whereas probably +# NOTE: one of the -f{freestanding, no-hosted, no-builtin} options should work. +# NOTE: See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56888 + +CFLAGS = -O2 -Wall \ + -fno-unit-at-a-time -fno-zero-initialized-in-bss \ + -ffreestanding -fno-hosted -fno-builtin \ + -march=mips32 +LDFLAGS = -nostdlib -EL + +TARGET = vga.elf +DUMP = $(TARGET:.elf=.dump) +MAP = $(TARGET:.elf=.map) +SCRIPT = $(TARGET:.elf=.ld) + +HEX = $(TARGET:.elf=.hex) +SREC = $(TARGET:.elf=.srec) + +# Ordering of objects is important and cannot be left to replacement rules. + +SRC = vga.S +OBJ = vga.o + +.PHONY: all clean distclean + +all: $(HEX) $(SREC) + +clean: + rm -f $(OBJ) $(TARGET) $(HEX) $(SREC) $(DUMP) *.map + +distclean: clean + echo "Nothing else to clean." + +$(HEX): $(TARGET) + $(OBJCOPY) -O ihex $(TARGET) $(HEX) + +$(SREC): $(TARGET) + $(OBJCOPY) -O srec $(TARGET) $(SREC) + +$(TARGET): $(OBJ) + $(LD) $(LDFLAGS) -T $(SCRIPT) $(OBJ) -o $@ + $(OBJDUMP) -D $(TARGET) > $(DUMP) + $(OBJDUMP) -h $(TARGET) > $(MAP) + $(NM) -n $(TARGET) > System.map + +.c.o: + $(CC) -c $(CFLAGS) $< -o $@ + +.S.o: + $(CC) -c $(CFLAGS) $< -o $@ diff -r 000000000000 -r 71d6504cb824 mips.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mips.h Sat May 06 17:53:56 2017 +0200 @@ -0,0 +1,49 @@ +#ifndef __MIPS_H__ +#define __MIPS_H__ + +#define CP0_INDEX $0 +#define CP0_ENTRYLO0 $2 +#define CP0_ENTRYLO1 $3 +#define CP0_CONTEXT $4 +#define CP0_PAGEMASK $5 +#define CP0_WIRED $6 +#define CP0_ENTRYHI $10 +#define CP0_STATUS $12 +#define CP0_INTCTL $12, 1 +#define CP0_CAUSE $13 +#define CP0_EPC $14 +#define CP0_EBASE $15, 1 +#define CP0_CONFIG $16 +#define CP0_WATCHLO $18 +#define CP0_TAGLO $28 +#define CP0_TAGHI $29 + +#define STATUS_CP0 0x10000000 +#define STATUS_BEV 0x00400000 +#define STATUS_IRQ 0x0000fc00 +#define STATUS_UM 0x00000010 +#define STATUS_ERL 0x00000004 +#define STATUS_EXL 0x00000002 +#define STATUS_IE 0x00000001 + +#define CAUSE_IV 0x00800000 + +#define EBASE_MASK 0x3ffff000 + +#define INTCTL_MASK 0x000003e0 + +#define TLB_CACHED 0x00000018 +#define TLB_UNCACHED 0x00000010 +#define TLB_DIRTY 0x00000004 +#define TLB_VALID 0x00000002 +#define TLB_GLOBAL 0x00000001 + +#define TLB_READ (TLB_CACHED | TLB_VALID) +#define TLB_WRITE (TLB_CACHED | TLB_DIRTY | TLB_VALID) +#define TLB_ALL_READ (TLB_CACHED | TLB_VALID | TLB_GLOBAL) +#define TLB_ALL_WRITE (TLB_CACHED | TLB_DIRTY | TLB_VALID | TLB_GLOBAL) + +#define CONFIG_CM_UNCACHED 2 +#define CONFIG_CM_CACHABLE_NONCOHERENT 3 + +#endif /* __MIPS_H__ */ diff -r 000000000000 -r 71d6504cb824 pic32.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pic32.h Sat May 06 17:53:56 2017 +0200 @@ -0,0 +1,53 @@ +#ifndef __PIC32_H__ +#define __PIC32_H__ + +/* See... + * TABLE 4-1: SFR MEMORYMAP + * TABLE 11-3: PORTA REGISTER MAP + * 11.2 CLR, SET and INV Registers + * PIC32MX1XX/2XX 28/36/44-pin Family Data Sheet + */ + +#define T1CON 0xBF800600 +#define TMR1 0xBF800610 +#define PR1 0xBF800620 + +#define PMCON 0xBF807000 +#define PMMODE 0xBF807010 +#define PMADDR 0xBF807020 +#define PMDOUT 0xBF807030 +#define PMDIN 0xBF807040 +#define PMAEN 0xBF807050 +#define PMSTAT 0xBF807060 + +#define OSCCON 0xBF80F000 +#define CFGCON 0xBF80F200 +#define SYSKEY 0xBF80F230 + +#define IFS0 0xBF881030 +#define IFS1 0xBF881040 +#define IEC0 0xBF881060 +#define IEC1 0xBF881070 +#define IPC1 0xBF8810A0 +#define IPC7 0xBF881100 +#define IPC8 0xBF881110 + +#define BMXCON 0xBF882000 +#define BMXDRMSZ 0xBF882040 + +#define ANSELA 0xBF886000 +#define TRISA 0xBF886010 +#define PORTA 0xBF886020 +#define LATA 0xBF886030 +#define ODCA 0xBF886040 +#define ANSELB 0xBF886100 +#define TRISB 0xBF886110 +#define PORTB 0xBF886120 +#define LATB 0xBF886130 +#define ODCB 0xBF886140 + +#define CLR 0x4 +#define SET 0x8 +#define INV 0xC + +#endif /* __PIC32_H__ */ diff -r 000000000000 -r 71d6504cb824 vga.S --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vga.S Sat May 06 17:53:56 2017 +0200 @@ -0,0 +1,277 @@ +/* + * Generate a VGA signal using a PIC32 microcontroller. + * + * Copyright (C) 2017 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 "mips.h" +#include "pic32.h" + +#define HFREQ_LIMIT 627 /* 20MHz cycles */ + +/* Disable JTAG functionality on pins. */ + +.section .devcfg0, "a" +.word 0xfffffffb /* DEVCFG0<2> = JTAGEN = 0 */ + +/* +Set the oscillator to be the FRC oscillator with PLL, with peripheral clock +divided by 1, HS oscillator mode selected (for PLL), and FRCDIV+PLL selected. +With a system clock of 20MHz, the peripheral clock is therefore 20MHz. + +The watchdog timer (FWDTEN) is also disabled. +*/ + +.section .devcfg1, "a" +.word 0xff7fcff9 /* DEVCFG1<23> = FWDTEN = 0; DEVCFG1<13:12> = FPBDIV<2:0> = 0; DEVCFG1<2:0> = FNOSC<2:0> = 001 */ + +/* +Set the FRC oscillator PLL function with an input division of 4, an output +division of 2, a multiplication of 20, yielding a multiplication of 2.5. +This should take the FRC at 8MHz and produce a system clock of 20MHz. +*/ + +.section .devcfg2, "a" +.word 0xfff9ffdb /* DEVCFG2<18:16> = FPLLODIV<2:0> = 1; DEVCFG2<6:4> = FPLLMUL<2:0> = 101; DEVCFG2<2:0> = FPLLIDIV<2:0> = 011 */ + +.text +.globl _start + +_start: + /* + Configure RAM. + See: http://microchipdeveloper.com/32bit:mx-arch-exceptions-processor-initialization + */ + + la $v0, BMXCON + li $v1, (1 << 6) /* BMXCON<6> = BMXWSDRM = 0 */ + sw $v1, CLR($v0) + + /* Enable caching. */ + + li $v0, CONFIG_CM_CACHABLE_NONCOHERENT + mtc0 $v0, CP0_CONFIG + nop + + /* Get the RAM size. */ + + la $v0, BMXDRMSZ + lw $v0, 0($v0) + + /* Initialise the stack pointer. */ + + lui $v1, 0x8000 /* 0x80000000 */ + addu $sp, $v0, $v1 /* sp = 0x80000000 + RAM size */ + + /* Initialise the globals pointer. */ + + lui $gp, %hi(_GLOBAL_OFFSET_TABLE_) + ori $gp, $gp, %lo(_GLOBAL_OFFSET_TABLE_) + + /* Set pins for output and set outputs low. */ + + jal init_pins + nop + + /* Initialise the status register. */ + + jal init_interrupts + nop + + /* Initialise timer. */ + + jal init_timer1 + nop + + /* Enable interrupts and loop. */ + + jal enable_interrupts + nop + + jal handle_error_level + nop + + li $a1, 20000000 /* counter = 20000000 */ + li $a2, 31872 +loop: + addiu $a1, $a1, -1 /* counter -= 1 */ + bnez $a1, loop /* until counter == 0 */ + nop + + li $a1, 20000000 /* counter = 20000000 */ + + la $v0, PORTB + la $v1, (1 << 10) /* PORTB<10> = RB10 */ + sw $v1, INV($v0) + +_next: + j loop + nop + + + +init_pins: + /* DEVCFG<2> needs setting to 0 before the program is run. */ + + la $v0, CFGCON + li $v1, (1 << 3) /* CFGCON<3> = JTAGEN = 0 */ + sw $v1, CLR($v0) + + la $v0, TRISB + li $v1, (7 << 7) | 15 /* TRISB<9:7> = RB9, RB8, RB7 = 0 */ + /* sw $v1, CLR($v0) */ + + la $v0, PORTB + li $v1, (7 << 7) | 15 /* PORTB<9:7> = RB9, RB8, RB7 = 0 */ + /* sw $v1, CLR($v0) */ + +init_leds: + la $v0, TRISA + li $v1, (1 << 2) /* TRISA<2> = RA2 = 0 */ + sw $v1, CLR($v0) + + la $v0, TRISB + li $v1, (3 << 10) /* TRISB<11:10> = RB11, RB10 = 0 */ + sw $v1, CLR($v0) + +clear_leds: + la $v0, PORTA + li $v1, (1 << 2) /* PORTA<2> = RA2 = 0 */ + sw $v1, CLR($v0) + + la $v0, PORTB + li $v1, (3 << 10) /* PORTB<11:10> = RB11, RB10 = 0 */ + sw $v1, CLR($v0) + + jr $ra + nop + + + +/* Utilities. */ + +handle_error_level: + mfc0 $t3, CP0_STATUS + li $t4, ~(STATUS_ERL | STATUS_EXL) + and $t3, $t3, $t4 + mtc0 $t3, CP0_STATUS + jr $ra + nop + +enable_interrupts: + mfc0 $t3, CP0_STATUS + nop + ori $t3, $t3, STATUS_IRQ | STATUS_IE + mtc0 $t3, CP0_STATUS + jr $ra + nop + +init_interrupts: + mfc0 $t3, CP0_STATUS + li $t4, ~STATUS_BEV + and $t3, $t3, $t4 + mtc0 $t3, CP0_STATUS /* CP0_STATUS &= ~STATUS_BEV (use non-bootloader vectors) */ + + la $t3, _start + mtc0 $t3, CP0_EBASE /* EBASE = _start */ + + li $t3, CAUSE_IV /* IV = 1 (use EBASE+0x200 for interrupts) */ + mtc0 $t3, CP0_CAUSE + + jr $ra + nop + + + +/* Interrupt servicing. */ + +.org 0x200 + +interrupt_handler: + + /* Check for a timer interrupt condition. */ + + la $v0, IFS0 + lw $v1, 0($v0) + and $v1, $v1, (1 << 4) /* T1IF */ + beqz $v1, irq_exit + nop + + addiu $a2, $a2, -1 + bnez $a2, irq_clear + nop + + li $a2, 31872 + + /* Invert an LED. */ + + la $v0, PORTB + la $v1, (1 << 11) /* PORTB<11> = RB11 */ + sw $v1, INV($v0) + +irq_clear: + + /* Clear the timer interrupt condition. */ + + la $v0, IFS0 + li $v1, (1 << 4) /* IFS0<4> = T1IF = 0 */ + sw $v1, CLR($v0) + +irq_exit: + eret + nop + + + +/* Initialisation routines. */ + +init_timer1: + + /* Initialise Timer1 interrupt. */ + + la $v0, T1CON + sw $zero, 0($v0) /* T1CON = 0 */ + nop + + la $v0, TMR1 + sw $zero, 0($v0) /* TMR1 = 0 */ + la $v0, PR1 + li $v1, HFREQ_LIMIT + sw $v1, 0($v0) /* PR1 = HFREQ_LIMIT */ + + /* Initialise Timer1 interrupt. */ + + la $v0, IFS0 + li $v1, (1 << 4) + sw $v1, CLR($v0) /* IFS0CLR: T1IF = 0 */ + la $v0, IPC1 + li $v1, (7 << 2) + sw $v1, SET($v0) /* IPC1SET: T1IP = 7 */ + la $v0, IPC1 + li $v1, 3 + sw $v1, SET($v0) /* IPC1SET: T1IS = 3 */ + la $v0, IEC0 + li $v1, (1 << 4) + sw $v1, SET($v0) /* IEC0SET: T1IE = 1 */ + + /* Start timer. */ + + la $v0, T1CON + li $v1, (1 << 15) /* ON = 1 */ + sw $v1, SET($v0) /* T1CONSET: ON = 1 */ + + jr $ra + nop diff -r 000000000000 -r 71d6504cb824 vga.ld --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vga.ld Sat May 06 17:53:56 2017 +0200 @@ -0,0 +1,44 @@ +OUTPUT_ARCH(mips) +ENTRY(_start) + +/* See... + * FIGURE 4-5: MEMORY MAP ON RESET FOR PIC32MX170/270 DEVICES (64 KB RAM, 256 KB FLASH) + * PIC32MX1XX/2XX 28/36/44-pin Family Data Sheet + */ + +MEMORY +{ + kseg1_data_mem (w!x) : ORIGIN = 0xA0000000, LENGTH = 0x10000 + kseg0_program_mem (rx) : ORIGIN = 0x9FC00000, LENGTH = 0xBF0 + physical_program_mem (rx) : ORIGIN = 0x1FC00000, LENGTH = 0xBF0 + sfrs : ORIGIN = 0xBF800000, LENGTH = 0x100000 + configsfrs : ORIGIN = 0xBFC00BF0, LENGTH = 0x10 + config3 : ORIGIN = 0xBFC00BF0, LENGTH = 0x4 + config2 : ORIGIN = 0xBFC00BF4, LENGTH = 0x4 + config1 : ORIGIN = 0xBFC00BF8, LENGTH = 0x4 + config0 : ORIGIN = 0xBFC00BFC, LENGTH = 0x4 + physical_config3 : ORIGIN = 0x3FC00BF0, LENGTH = 0x4 + physical_config2 : ORIGIN = 0x3FC00BF4, LENGTH = 0x4 + physical_config1 : ORIGIN = 0x3FC00BF8, LENGTH = 0x4 + physical_config0 : ORIGIN = 0x3FC00BFC, LENGTH = 0x4 +} + +SECTIONS +{ + .text : { *(.text*) } > kseg0_program_mem AT > physical_program_mem + .bss : { *(.bss*) } > kseg1_data_mem + .got : { + _gp = ALIGN(16); + *(.got*) + } > kseg0_program_mem AT > physical_program_mem + .devcfg0 : { + *(.devcfg0) + } > config0 AT > physical_config0 + .devcfg1 : { + *(.devcfg1) + } > config1 AT > physical_config1 + .devcfg2 : { + *(.devcfg2) + } > config2 AT > physical_config2 + /DISCARD/ : { *(.reginfo) *(.MIPS.abiflags) } +}