1 /* 2 * Generate a VGA signal using a PIC32 microcontroller. 3 * 4 * Copyright (C) 2017 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "mips.h" 21 #include "pic32.h" 22 23 #define HFREQ_LIMIT 627 /* 20MHz cycles */ 24 25 /* Disable JTAG functionality on pins. */ 26 27 .section .devcfg0, "a" 28 .word 0xfffffffb /* DEVCFG0<2> = JTAGEN = 0 */ 29 30 /* 31 Set the oscillator to be the FRC oscillator with PLL, with peripheral clock 32 divided by 1, HS oscillator mode selected (for PLL), and FRCDIV+PLL selected. 33 With a system clock of 20MHz, the peripheral clock is therefore 20MHz. 34 35 The watchdog timer (FWDTEN) is also disabled. 36 */ 37 38 .section .devcfg1, "a" 39 .word 0xff7fcff9 /* DEVCFG1<23> = FWDTEN = 0; DEVCFG1<13:12> = FPBDIV<2:0> = 0; DEVCFG1<2:0> = FNOSC<2:0> = 001 */ 40 41 /* 42 Set the FRC oscillator PLL function with an input division of 4, an output 43 division of 2, a multiplication of 20, yielding a multiplication of 2.5. 44 This should take the FRC at 8MHz and produce a system clock of 20MHz. 45 */ 46 47 .section .devcfg2, "a" 48 .word 0xfff9ffdb /* DEVCFG2<18:16> = FPLLODIV<2:0> = 1; DEVCFG2<6:4> = FPLLMUL<2:0> = 101; DEVCFG2<2:0> = FPLLIDIV<2:0> = 011 */ 49 50 .text 51 .globl _start 52 53 _start: 54 /* 55 Configure RAM. 56 See: http://microchipdeveloper.com/32bit:mx-arch-exceptions-processor-initialization 57 */ 58 59 la $v0, BMXCON 60 li $v1, (1 << 6) /* BMXCON<6> = BMXWSDRM = 0 */ 61 sw $v1, CLR($v0) 62 63 /* Enable caching. */ 64 65 li $v0, CONFIG_CM_CACHABLE_NONCOHERENT 66 mtc0 $v0, CP0_CONFIG 67 nop 68 69 /* Get the RAM size. */ 70 71 la $v0, BMXDRMSZ 72 lw $v0, 0($v0) 73 74 /* Initialise the stack pointer. */ 75 76 lui $v1, 0x8000 /* 0x80000000 */ 77 addu $sp, $v0, $v1 /* sp = 0x80000000 + RAM size */ 78 79 /* Initialise the globals pointer. */ 80 81 lui $gp, %hi(_GLOBAL_OFFSET_TABLE_) 82 ori $gp, $gp, %lo(_GLOBAL_OFFSET_TABLE_) 83 84 /* Set pins for output and set outputs low. */ 85 86 jal init_pins 87 nop 88 89 /* Initialise the status register. */ 90 91 jal init_interrupts 92 nop 93 94 /* Initialise timer. */ 95 96 jal init_timer1 97 nop 98 99 /* Enable interrupts and loop. */ 100 101 jal enable_interrupts 102 nop 103 104 jal handle_error_level 105 nop 106 107 li $a1, 20000000 /* counter = 20000000 */ 108 li $a2, 31872 109 loop: 110 addiu $a1, $a1, -1 /* counter -= 1 */ 111 bnez $a1, loop /* until counter == 0 */ 112 nop 113 114 li $a1, 20000000 /* counter = 20000000 */ 115 116 la $v0, PORTB 117 la $v1, (1 << 10) /* PORTB<10> = RB10 */ 118 sw $v1, INV($v0) 119 120 _next: 121 j loop 122 nop 123 124 125 126 init_pins: 127 /* DEVCFG<2> needs setting to 0 before the program is run. */ 128 129 la $v0, CFGCON 130 li $v1, (1 << 3) /* CFGCON<3> = JTAGEN = 0 */ 131 sw $v1, CLR($v0) 132 133 la $v0, TRISB 134 li $v1, (7 << 7) | 15 /* TRISB<9:7> = RB9, RB8, RB7 = 0 */ 135 /* sw $v1, CLR($v0) */ 136 137 la $v0, PORTB 138 li $v1, (7 << 7) | 15 /* PORTB<9:7> = RB9, RB8, RB7 = 0 */ 139 /* sw $v1, CLR($v0) */ 140 141 init_leds: 142 la $v0, TRISA 143 li $v1, (1 << 2) /* TRISA<2> = RA2 = 0 */ 144 sw $v1, CLR($v0) 145 146 la $v0, TRISB 147 li $v1, (3 << 10) /* TRISB<11:10> = RB11, RB10 = 0 */ 148 sw $v1, CLR($v0) 149 150 clear_leds: 151 la $v0, PORTA 152 li $v1, (1 << 2) /* PORTA<2> = RA2 = 0 */ 153 sw $v1, CLR($v0) 154 155 la $v0, PORTB 156 li $v1, (3 << 10) /* PORTB<11:10> = RB11, RB10 = 0 */ 157 sw $v1, CLR($v0) 158 159 jr $ra 160 nop 161 162 163 164 /* Utilities. */ 165 166 handle_error_level: 167 mfc0 $t3, CP0_STATUS 168 li $t4, ~(STATUS_ERL | STATUS_EXL) 169 and $t3, $t3, $t4 170 mtc0 $t3, CP0_STATUS 171 jr $ra 172 nop 173 174 enable_interrupts: 175 mfc0 $t3, CP0_STATUS 176 nop 177 ori $t3, $t3, STATUS_IRQ | STATUS_IE 178 mtc0 $t3, CP0_STATUS 179 jr $ra 180 nop 181 182 init_interrupts: 183 mfc0 $t3, CP0_STATUS 184 li $t4, ~STATUS_BEV 185 and $t3, $t3, $t4 186 mtc0 $t3, CP0_STATUS /* CP0_STATUS &= ~STATUS_BEV (use non-bootloader vectors) */ 187 188 la $t3, _start 189 mtc0 $t3, CP0_EBASE /* EBASE = _start */ 190 191 li $t3, CAUSE_IV /* IV = 1 (use EBASE+0x200 for interrupts) */ 192 mtc0 $t3, CP0_CAUSE 193 194 jr $ra 195 nop 196 197 198 199 /* Interrupt servicing. */ 200 201 .org 0x200 202 203 interrupt_handler: 204 205 /* Check for a timer interrupt condition. */ 206 207 la $v0, IFS0 208 lw $v1, 0($v0) 209 and $v1, $v1, (1 << 4) /* T1IF */ 210 beqz $v1, irq_exit 211 nop 212 213 addiu $a2, $a2, -1 214 bnez $a2, irq_clear 215 nop 216 217 li $a2, 31872 218 219 /* Invert an LED. */ 220 221 la $v0, PORTB 222 la $v1, (1 << 11) /* PORTB<11> = RB11 */ 223 sw $v1, INV($v0) 224 225 irq_clear: 226 227 /* Clear the timer interrupt condition. */ 228 229 la $v0, IFS0 230 li $v1, (1 << 4) /* IFS0<4> = T1IF = 0 */ 231 sw $v1, CLR($v0) 232 233 irq_exit: 234 eret 235 nop 236 237 238 239 /* Initialisation routines. */ 240 241 init_timer1: 242 243 /* Initialise Timer1 interrupt. */ 244 245 la $v0, T1CON 246 sw $zero, 0($v0) /* T1CON = 0 */ 247 nop 248 249 la $v0, TMR1 250 sw $zero, 0($v0) /* TMR1 = 0 */ 251 la $v0, PR1 252 li $v1, HFREQ_LIMIT 253 sw $v1, 0($v0) /* PR1 = HFREQ_LIMIT */ 254 255 /* Initialise Timer1 interrupt. */ 256 257 la $v0, IFS0 258 li $v1, (1 << 4) 259 sw $v1, CLR($v0) /* IFS0CLR: T1IF = 0 */ 260 la $v0, IPC1 261 li $v1, (7 << 2) 262 sw $v1, SET($v0) /* IPC1SET: T1IP = 7 */ 263 la $v0, IPC1 264 li $v1, 3 265 sw $v1, SET($v0) /* IPC1SET: T1IS = 3 */ 266 la $v0, IEC0 267 li $v1, (1 << 4) 268 sw $v1, SET($v0) /* IEC0SET: T1IE = 1 */ 269 270 /* Start timer. */ 271 272 la $v0, T1CON 273 li $v1, (1 << 15) /* ON = 1 */ 274 sw $v1, SET($v0) /* T1CONSET: ON = 1 */ 275 276 jr $ra 277 nop