# HG changeset patch # User Paul Boddie # Date 1494367699 -7200 # Node ID 86fe4c03b489e13bf8c7b943ec9588ca5dbd74e3 # Parent 0cf01717e7ef225f998c5df0ec78dcb0b653d644 Tidied and incorporated some aspects of working PMP and DMA tests. diff -r 0cf01717e7ef -r 86fe4c03b489 vga.S --- a/vga.S Wed May 10 00:07:28 2017 +0200 +++ b/vga.S Wed May 10 00:08:19 2017 +0200 @@ -21,6 +21,9 @@ #include "pic32.h" #define HFREQ_LIMIT 627 /* 20MHz cycles */ +#define HSYNC_LIMIT 16 /* bytes or pixels */ +#define LINE_LIMIT 160 /* bytes or pixels */ + #define VSYNC_START 1 /* horizontal lines, front porch end */ #define VISIBLE_START 4 /* horizontal lines */ #define VBP_START 516 /* horizontal lines, back porch start */ @@ -72,13 +75,13 @@ /* Get the RAM size. */ - la $v0, BMXDRMSZ + la $v0, BMXDRMSZ lw $v0, 0($v0) /* Initialise the stack pointer. */ - lui $v1, 0x8000 /* 0x80000000 */ - addu $sp, $v0, $v1 /* sp = 0x80000000 + RAM size */ + li $v1, KSEG0_BASE + addu $sp, $v0, $v1 /* sp = KSEG0_BASE + RAM size */ /* Initialise the globals pointer. */ @@ -100,6 +103,21 @@ jal init_timer1 nop + /* Initialise PMP. */ + + jal init_pmp + nop + + /* Initialise DMA. */ + + jal init_dma + nop + + /* Initialise framebuffer. */ + + jal init_framebuffer + nop + /* Enable interrupts and loop. */ jal enable_interrupts @@ -108,20 +126,27 @@ jal handle_error_level nop + /* Main program. */ + li $a1, 20000000 /* counter = 20000000 */ - li $a2, 6374 /* test timer scaling */ + li $a2, 3187 /* test timer scaling */ + + /* Initialise the display state. */ + li $s0, 0 /* line counter */ la $s1, vfp_active /* current event */ + + /* Monitoring loop. */ loop: - addiu $a1, $a1, -1 /* counter -= 1 */ - bnez $a1, loop /* until counter == 0 */ + addiu $a1, $a1, -1 /* counter -= 1 */ + bnez $a1, loop /* until counter == 0 */ nop li $a1, 20000000 /* counter = 20000000 */ - la $v0, PORTA - la $v1, (1 << 2) /* PORTA<2> = RA2 */ - sw $v1, INV($v0) + la $t0, PORTB + la $t1, (1 << 10) /* PORTB<10> = RB10 */ + sw $t1, INV($t0) _next: j loop @@ -132,37 +157,87 @@ init_pins: /* DEVCFG0<2> needs setting to 0 before the program is run. */ - la $v0, CFGCON + la $v0, CFGCON li $v1, (1 << 3) /* CFGCON<3> = JTAGEN = 0 */ sw $v1, CLR($v0) +init_outputs: + /* Remove analogue features from pins. */ + + la $v0, ANSELA + sw $zero, 0($v0) /* ANSELA = 0 */ + la $v0, ANSELB + sw $zero, 0($v0) /* ANSELB = 0 */ + + la $v0, TRISA + sw $zero, 0($v0) la $v0, TRISB - li $v1, (7 << 7) | 15 /* TRISB<9:7> = RB9, RB8, RB7 = 0 */ - /* sw $v1, CLR($v0) */ + sw $zero, 0($v0) + + jr $ra + nop + + + +/* Interrupt servicing. */ + +.org 0x200 + +interrupt_handler: + + /* Check for a timer interrupt condition. */ - la $v0, PORTB - li $v1, (7 << 7) | 15 /* PORTB<9:7> = RB9, RB8, RB7 = 0 */ - /* sw $v1, CLR($v0) */ + la $v0, IFS0 + lw $v1, 0($v0) + andi $v1, $v1, (1 << 4) /* T1IF */ + beqz $v1, irq_next + nop + + /* Timer scaling for testing purposes. */ + + addiu $a2, $a2, -1 + bnez $a2, irq_clear_timer + nop + li $a2, 3187 -init_leds: - la $v0, TRISA - li $v1, (1 << 2) /* TRISA<2> = RA2 = 0 */ + /* Increment the line counter. */ + + addiu $s0, $s0, 1 + + /* Jump to the event handler. */ + + jalr $s1 + nop + +irq_clear_timer: + + /* Clear the timer interrupt condition. */ + + la $v0, IFS0 + li $v1, (1 << 4) /* IFS0<4> = T1IF = 0 */ sw $v1, CLR($v0) - la $v0, TRISB - li $v1, (3 << 10) /* TRISB<11:10> = RB11, RB10 = 0 */ +irq_next: + + /* Check for a PMP interrupt condition. */ + + la $v0, IFS1 + lw $v1, 0($v0) + li $t8, (1 << 16) /* PMPIF */ + and $v1, $v1, $t8 + beqz $v1, irq_exit + nop + +irq_clear_pmp: + + /* Clear the PMP interrupt condition. */ + + la $v0, IFS1 + li $v1, (1 << 16) /* IFS1<16> = PMPIF = 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 +irq_exit: + eret nop @@ -202,46 +277,120 @@ -/* Interrupt servicing. */ +/* Event routines. */ + +/* Start of, and within, the vertical front porch. */ + +vfp_start: + /* Clear vsync. */ + + la $v0, PORTB + la $v1, (1 << 11) /* PORTB<11> = RB11 */ + sw $v1, CLR($v0) -.org 0x200 + /* Enter the active region. */ + + la $s1, vfp_active -interrupt_handler: +vfp_active: + /* Test for vsync. */ + + subu $v0, $s0, VSYNC_START + bnez $v0, _vfp_active_ret + nop + + /* Start the vsync region. */ - /* Check for a timer interrupt condition. */ + la $s1, vsync_start + +_vfp_active_ret: + jr $ra + nop + + + +/* Start of, and within, vsync. */ + +vsync_start: + /* Set vsync. */ - la $v0, IFS0 - lw $v1, 0($v0) - and $v1, $v1, (1 << 4) /* T1IF */ - beqz $v1, irq_exit + la $v0, PORTB + la $v1, (1 << 11) /* PORTB<11> = RB11 */ + sw $v1, SET($v0) + + /* Enter the active region. */ + + la $s1, vsync_active + +vsync_active: + /* Test for visible region. */ + + subu $v0, $s0, VISIBLE_START + bnez $v0, _vsync_active_ret nop - /* Test timer scaling. */ - - addiu $a2, $a2, -1 - bnez $a2, irq_clear - nop - li $a2, 6374 + /* Start the visible region. */ - /* Increment the line counter. */ - - addiu $s0, $s0, 1 + la $s1, visible_start - /* Jump to the event handler. */ - - jalr $s1 +_vsync_active_ret: + jr $ra nop -irq_clear: + + +/* Start of, and within, the visible period. */ - /* Clear the timer interrupt condition. */ +visible_start: + /* Clear vsync. */ - la $v0, IFS0 - li $v1, (1 << 4) /* IFS0<4> = T1IF = 0 */ + la $v0, PORTB + la $v1, (1 << 11) /* PORTB<11> = RB11 */ sw $v1, CLR($v0) -irq_exit: - eret + /* Enter the active region. */ + + la $s1, visible_active + +visible_active: + /* Initiate hsync transfer and subsequent line transfer. */ + + la $v0, DCH0ECON + li $v1, (1 << 7) + sw $v1, SET($v0) + + /* Test for back porch. */ + + subu $v0, $s0, VBP_START + bnez $v0, _visible_active_ret + nop + + /* Start the back porch. */ + + la $s1, vbp_active + +_visible_active_ret: + jr $ra + nop + + + +/* Within the vertical back porch. */ + +vbp_active: + /* Test for front porch. */ + + subu $v0, $s0, VBP_END + bnez $v0, _vbp_active_ret + nop + + /* Start the front porch. */ + + li $s0, 0 + la $s1, vfp_start + +_vbp_active_ret: + jr $ra nop @@ -288,118 +437,301 @@ -/* Event routines. */ - -/* Start of, and within, the vertical front porch. */ +/* Parallel Master Port initialisation. */ -vfp_start: - /* Clear vsync. */ +init_pmp: + /* Disable PMP interrupts. */ - la $v0, PORTB - la $v1, (1 << 10) /* PORTB<10> = RB10 */ + la $v0, IEC1 + li $v1, (1 << 16) /* IEC1<16> = PMPIE = 0 */ sw $v1, CLR($v0) - /* Enter the active region. */ + /* Initialise PMP. + + PMCON<12:11> = ADDRMUX<1:0> = 0; demultiplexed address and data + PMCON<9> = PTWREN<0> = 0; no write pin + PMCON<8> = PTRDEN<0> = 0; no read pin + PMCON<7:6> = CSF<1:0> = 0; no chip select pins + */ - la $s1, vfp_active + la $v0, PMCON + sw $zero, 0($v0) -vfp_active: - /* Test for vsync. */ + /* + PMMODE<14:13> = IRQM<1:0> = 1; interrupt after every read/write + PMMODE<12:11> = INCM<1:0> = 0; no increment on every read/write + PMMODE<10> = MODE16<0> = 0; 8-bit transfers + PMMODE<9:8> = MODE<1:0> = 10; master mode 2 + PMMODE<5:2> = WAITM<3:0> = 00; single cycle read/write, no chip select wait cycles + */ - subu $v0, $s0, VSYNC_START - bnez $v0, _vfp_active_ret - nop + la $v0, PMMODE + li $v1, 0x2200 + sw $v1, 0($v0) + + /* Free non-essential pins for general I/O. */ + + la $v0, PMAEN + sw $zero, 0($v0) - /* Start the vsync region. */ + la $v0, PMADDR + sw $zero, 0($v0) + + la $v0, IFS1 + li $v1, (3 << 16) /* IFS1<17:16> = PMPEIF, PMPIF = 0 */ + sw $v1, CLR($v0) - la $s1, vsync_start + /* Start PMP mode. */ -_vfp_active_ret: + la $v0, PMCON + li $v1, (1 << 15) /* PMCON<15> = ON = 1 */ + sw $v1, SET($v0) + jr $ra nop -/* Start of, and within, vsync. */ +/* Direct Memory Access initialisation. */ + +/* +Write 16 pixels to the PMP for a hsync pulse. This channel is invoked +explicitly in the interrupt handler for Timer1 since it will not happen on every +occurrence of the interrupt, but only on those occurrences within the active +region of the display output. + +Write 160 pixels to the PMP for the line data. This is initiated by the +completion of the hsync transfer. +*/ + +init_dma: + /* Disable DMA interrupts. */ -vsync_start: - /* Set vsync. */ + la $v0, IEC1 + li $v1, (7 << 28) /* IEC1<30:28> = DMA2IE, DMA1IE, DMA0IE = 0 */ + sw $v1, CLR($v0) + + /* Clear DMA interrupt flags. */ - la $v0, PORTB - la $v1, (1 << 10) /* PORTB<10> = RB10 */ + la $v0, IFS1 + li $v1, (7 << 28) /* IFS1<30:28> = DMA2IF, DMA1IF, DMA0IF = 0 */ + sw $v1, CLR($v0) + + /* Enable DMA. */ + + la $v0, DMACON + li $v1, (1 << 15) sw $v1, SET($v0) - /* Enter the active region. */ + /* + Initialise a hsync channel and a line channel. + The hsync channel will be channel 0 (x = 0). + The line channel will be channel 1 (x = 1). + + Once the hsync channel has completed a transfer, the line channel + transfer is initiated. + + Specify a priority of 3: + DCHxCON<1:0> = CHPRI<1:0> = 3 + + Auto-enable the channels: + DCHxCON<4> = CHAEN = 1 + */ + + la $v0, DCH0CON + li $v1, 0b10011 + sw $v1, 0($v0) + + la $v0, DCH1CON + li $v1, 0b10011 + sw $v1, 0($v0) + + la $v0, DCH2CON + li $v1, 0b10011 + sw $v1, 0($v0) + + /* Initiate channel transfers when the preceding channel interrupt occurs. */ + + la $v0, DCH0ECON + sw $zero, 0($v0) + + la $v0, DCH1ECON + li $v1, (60 << 8) | (1 << 4) + sw $v1, 0($v0) + + la $v0, DCH2ECON + li $v1, (61 << 8) | (1 << 4) + sw $v1, 0($v0) - la $s1, vsync_active + /* + The hsync channels have a cell size of 16 bytes: + DCHxCSIZ<15:0> = CHCSIZ<15:0> = HSYNC_LIMIT + + The line channel has a cell size of 160 bytes: + DCHxCSIZ<15:0> = CHCSIZ<15:0> = LINE_LIMIT + */ + + la $v0, DCH0CSIZ + li $v1, HSYNC_LIMIT + sw $v1, 0($v0) + + la $v0, DCH1CSIZ + li $v1, HSYNC_LIMIT + sw $v1, 0($v0) + + la $v0, DCH2CSIZ + li $v1, LINE_LIMIT + sw $v1, 0($v0) -vsync_active: - /* Test for visible region. */ + /* + Each source has a size identical to the cell size: + DCHxSSIZ<15:0> = CHSSIZ<15:0> = n + */ + + la $v0, DCH0SSIZ + li $v1, HSYNC_LIMIT + sw $v1, 0($v0) + + la $v0, DCH1SSIZ + li $v1, HSYNC_LIMIT + sw $v1, 0($v0) + + la $v0, DCH2SSIZ + li $v1, LINE_LIMIT + sw $v1, 0($v0) + + /* + The source address is the physical address of either the hsync pulse + data or the line data: + DCHxSSA = pulse data physical address + DCHxSSA = line data physical address + */ - subu $v0, $s0, VISIBLE_START - bnez $v0, _vsync_active_ret - nop + la $v0, DCH0SSA + la $v1, set_ra2 + li $t8, KSEG0_BASE + subu $v1, $v1, $t8 + sw $v1, 0($v0) + + la $v0, DCH1SSA + sw $v1, 0($v0) + + la $v0, DCH2SSA + sw $zero, 0($v0) + + /* + Each destination has a size of 1 byte: + DCHxDSIZ<15:0> = CHDSIZ<15:0> = 1 + */ + + la $v0, DCH0DSIZ + li $v1, 1 + sw $v1, 0($v0) + + la $v0, DCH1DSIZ + sw $v1, 0($v0) + + la $v0, DCH2DSIZ + sw $v1, 0($v0) + + /* + For the hsync channels, the destination address is the physical address + for an I/O register that sets the output signal for hsync: + DCHxDSA = physical(PORTA) + */ + + la $v0, DCH0DSA + li $v1, PORTA + INV + li $t8, KSEG1_BASE + subu $v1, $v1, $t8 + sw $v1, 0($v0) - /* Start the visible region. */ + la $v0, DCH1DSA + li $v1, PORTA + CLR + li $t8, KSEG1_BASE + subu $v1, $v1, $t8 + sw $v1, 0($v0) + + /* + For the line channel, the destination address is the physical address of + PMDIN: DCHxDSA = physical(PMDIN) + */ + + la $v0, DCH2DSA + li $v1, PMDIN + li $t8, KSEG1_BASE + subu $v1, $v1, $t8 + sw $v1, 0($v0) + + /* + The block transfer complete interrupt needs to be enabled, so that upon + completion of the transfer, the next transfer can be initiated: + DCHxINT<19> = CHBCIE = 1 + */ - la $s1, visible_start + la $v0, DCH0INT + li $v1, (1 << 19) + sw $v1, 0($v0) + + la $v0, DCH1INT + li $v1, (1 << 19) + sw $v1, 0($v0) + + la $v0, DCH2INT + sw $zero, 0($v0) -_vsync_active_ret: + /* Enable channels. */ + + la $v0, DCH0CON + li $v1, 0b10000000 + sw $v1, SET($v0) + + la $v0, DCH1CON + sw $v1, SET($v0) + + la $v0, DCH2CON + sw $v1, SET($v0) + jr $ra nop -/* Start of, and within, the visible period. */ - -visible_start: - /* Clear vsync. */ - - la $v0, PORTB - la $v1, (1 << 10) /* PORTB<10> = RB10 */ - sw $v1, CLR($v0) - - /* Enter the active region. */ +/* Framebuffer initialisation. */ - la $s1, visible_active - -visible_active: - /* NOTE: Perform line activities. */ +init_framebuffer: + li $v0, KSEG0_BASE + li $t8, 40 * 1024 + li $v1, 0xff - la $v0, PORTB - la $v1, (1 << 11) /* PORTB<11> = RB11 */ - sw $v1, INV($v0) - - /* Test for back porch. */ - - subu $v0, $s0, VBP_START - bnez $v0, _visible_active_ret +_init_fb_loop: + sw $v1, 0($v0) + addiu $v0, $v0, 4 + addiu $t8, $t8, -4 + bnez $t8, _init_fb_loop nop - /* Start the back porch. */ - - la $s1, vbp_active - -_visible_active_ret: jr $ra nop -/* Within the vertical back porch. */ - -vbp_active: - /* Test for front porch. */ - - subu $v0, $s0, VBP_END - bnez $v0, _vbp_active_ret - nop +/* 16 bytes of pulse data used to set PORTA<2> = RA2. */ - /* Start the front porch. */ - - li $s0, 0 - la $s1, vfp_start - -_vbp_active_ret: - jr $ra - nop +set_ra2: +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2) +.byte (1 << 2)