Search
lxdream.org :: lxdream/src/sh4/dmac.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/dmac.c
changeset 975:007bf7eb944f
prev929:fd8cb0c82f5f
author nkeynes
date Fri Dec 02 18:18:04 2011 +1000 (12 years ago)
permissions -rw-r--r--
last change SH4 shadow-mode tweaks
- Fix exceptions generated by the translator to account for the excepting
instruction(s) in the cycle counts.
- Compare floating point regs bitwise rather than with FP comparisons
(otherwise can fail due to nan != nan)
- Dump the translated block when we abort with an inconsistency
view annotate diff log raw
     1 /**
     2  * $Id$
     3  * 
     4  * SH4 onboard DMA controller (DMAC) peripheral.
     5  *
     6  * Copyright (c) 2005 Nathan Keynes.
     7  *
     8  * This program is free software; you can redistribute it and/or modify
     9  * it under the terms of the GNU General Public License as published by
    10  * the Free Software Foundation; either version 2 of the License, or
    11  * (at your option) any later version.
    12  *
    13  * This program is distributed in the hope that it will be useful,
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16  * GNU General Public License for more details.
    17  */
    18 #define MODULE sh4_module
    20 #include "dream.h"
    21 #include "mem.h"
    22 #include "sh4/sh4core.h"
    23 #include "sh4/sh4mmio.h"
    24 #include "sh4/intc.h"
    25 #include "sh4/dmac.h"
    27 static int DMAC_xfer_size[8] = {8, 1, 2, 4, 32, 1, 1, 1};
    29 /* Control flags */
    30 #define CHCR_IE 0x04  /* Interrupt Enable */
    31 #define CHCR_TE 0x02  /* Transfer End */
    32 #define CHCR_DE 0x01  /* DMAC Enable */
    34 #define IS_DMAC_ENABLED() ((MMIO_READ(DMAC,DMAOR)&0x07) == 0x01)
    36 #define IS_AUTO_REQUEST(val) ((val & 0x0C00) == 0x0400)
    37 #define CHANNEL_RESOURCE(val) ((val >> 8) & 0x0F)
    39 #define DMA_SOURCE(chan) MMIO_READ(DMAC, SAR0 + (chan<<4))
    40 #define DMA_DEST(chan) MMIO_READ(DMAC, DAR0 + (chan<<4))
    41 #define DMA_COUNT(chan) (MMIO_READ(DMAC, DMATCR0 + (chan<<4)) & 0x00FFFFFF)
    42 #define DMA_CONTROL(chan) MMIO_READ(DMAC, CHCR0 + (chan<<4))
    43 #define IS_CHANNEL_ENABLED(ctrl) ((ctrl & 0x03) == 0x01)
    44 #define IS_CHANNEL_IRQ_ENABLED(ctrl) (ctrl & CHCR_IE)
    45 #define IS_CHANNEL_IRQ_ACTIVE(ctrl) ((ctrl & (CHCR_IE|CHCR_TE)) == (CHCR_IE|CHCR_TE))
    47 #define DMARES_MEMORY_TO_MEMORY 0x00
    48 #define DMARES_MEMORY_TO_DEVICE 0x02
    49 #define DMARES_DEVICE_TO_MEMORY 0x03
    50 #define DMARES_MEMORY_TO_MEMORY_AUTO 0x04
    51 #define DMARES_MEMORY_TO_PERIPH_AUTO 0x05
    52 #define DMARES_PERIPH_TO_MEMORY_AUTO 0x06
    53 #define DMARES_SCI_TRANSMIT_EMPTY 0x08
    54 #define DMARES_SCI_RECEIVE_FULL 0x09
    55 #define DMARES_SCIF_TRANSMIT_EMPTY 0x0A
    56 #define DMARES_SCIF_RECEIVE_FULL 0x0B
    57 #define DMARES_MEMORY_TO_MEMORY_TMU 0x0C
    58 #define DMARES_MEMORY_TO_PERIPH_TMU 0x0D
    59 #define DMARES_PERIPH_TO_MEMORY_TMU 0x0E
    61 void DMAC_set_control( uint32_t channel, uint32_t val ) 
    62 {
    63     uint32_t oldval = DMA_CONTROL(channel);
    64     int resource;
    65     MMIO_WRITE( DMAC, CHCR0 + (channel<<4), val );
    67     /* If TE or IE are cleared, clear the interrupt request */
    68     if( IS_CHANNEL_IRQ_ACTIVE(oldval) &&
    69             !IS_CHANNEL_IRQ_ACTIVE(val) )
    70         intc_clear_interrupt( INT_DMA_DMTE0+channel );
    72     resource = CHANNEL_RESOURCE(val);
    73     if( IS_CHANNEL_ENABLED(val) ) {
    74         if( resource >= DMARES_MEMORY_TO_MEMORY_AUTO && 
    75                 resource < DMARES_SCI_TRANSMIT_EMPTY ) {
    76             /* Autorun */
    77         }
    78     }
    80     /* Everything else we don't need to care about until we actually try to
    81      * run the channel
    82      */
    83 }
    85 MMIO_REGION_READ_FN( DMAC, reg )
    86 {
    87     return MMIO_READ( DMAC, reg&0xFFF );
    88 }
    89 MMIO_REGION_READ_DEFSUBFNS(DMAC)
    91 MMIO_REGION_WRITE_FN( DMAC, reg, val )
    92 {
    93     reg &= 0xFFF;
    94     switch( reg ) {
    95     case DMAOR:
    96         MMIO_WRITE( DMAC, reg, val );
    97         break;
    98     case CHCR0: DMAC_set_control( 0, val ); break;
    99     case CHCR1: DMAC_set_control( 1, val ); break;
   100     case CHCR2: DMAC_set_control( 2, val ); break;
   101     case CHCR3: DMAC_set_control( 3, val ); break;
   102     default:
   103         MMIO_WRITE( DMAC, reg, val );
   104     }
   105 }
   107 /**
   108  * Execute up to run_count transfers on the specified channel. Assumes the
   109  * trigger for the channel has been received.
   110  *
   111  * @param channel Channel number (0-3) to run.
   112  * @param run_count number of transfers to execute, or 0 to run to the 
   113  * end of the transfer count.
   114  */
   115 void DMAC_run_channel( uint32_t channel, uint32_t run_count )
   116 {
   118 #if 0 /* Should really finish this */
   119     char burst[32]; /* Transfer burst */
   120     uint32_t control = DMA_CONTROL(channel);
   122     if( IS_CHANNEL_ENABLED(control) ) {
   123         uint32_t source = DMA_SOURCE(channel);
   124         uint32_t dest = DMA_DEST(channel);
   125         uint32_t count = DMA_COUNT( channel );
   126         if( count == 0 )
   127             count = 0x01000000;
   128         if( run_count == 0 || run_count > count )
   129             run_count = count;
   130         uint32_t xfersize = DMAC_xfer_size[ (control >> 4)&0x07 ];
   131         int source_step, dest_step;
   132         int resource = (control >> 8) & 0x0F;
   133         switch( (control >> 14) & 0x03 ) {
   134         case 0: dest_step = 0; break;
   135         case 1: dest_step = xfersize; break;
   136         case 2: dest_step = -xfersize; break;
   137         case 3: dest_step = 0; break; /* Illegal */
   138         }
   139         switch( (control >> 12) & 0x03 ) {
   140         case 0: source_step = 0; break;
   141         case 1: source_step = xfersize; break;
   142         case 2: source_step = -xfersize; break;
   143         case 3: source_step = 0; break; /* Illegal */
   144         }
   146         while( run_count > 0 ) {
   147             /* Origin */
   148             if( (resource & 0x02) == 0 ) {
   149                 /* Source is a normal memory address */
   151             } else {
   152                 /* Device */
   153             }
   155             /* Destination */
   156             if( (resource & 0x01) == 0 ) {
   157                 /* Destination is a normal memory address */
   158             } else {
   159             }
   160             run_count--; 
   161             count--;
   162         }
   163     }
   164 #endif
   165 }
   167 /**
   168  * Fetch a block of data by DMA from memory to an external device (ie the
   169  * ASIC). The DMA channel must be configured for Mem=>dev or it will return
   170  * no bytes and whinge mightily. Note this is NOT used for SH4 peripheral
   171  * transfers.
   172  *
   173  * @return the number of bytes actually transferred.
   174  */
   175 uint32_t DMAC_get_buffer( int channel, sh4ptr_t buf, uint32_t numBytes )
   176 {
   177     uint32_t control = DMA_CONTROL(channel);
   178     uint32_t source, count, run_count, size, i;
   179     char tmp[32];
   181     if( !IS_CHANNEL_ENABLED(control) || !IS_DMAC_ENABLED() )
   182         return 0;
   184     if( ((control >> 8) & 0x0F) !=  DMARES_MEMORY_TO_DEVICE ) {
   185         /* Error? */
   187         return 0;
   188     } 
   190     source = DMA_SOURCE(channel);
   191     count = DMA_COUNT(channel);
   192     if( count == 0 ) count = 0x01000000;
   194     size = DMAC_xfer_size[ (control >> 4)&0x07 ];
   195     run_count = numBytes / size;
   196     if( run_count > count || run_count == 0 )
   197         run_count = count;
   199     /* Do copy - FIXME: doesn't work when crossing regions */
   200     sh4ptr_t region = mem_get_region( source );
   201     switch( (control >> 12) & 0x03 ) {
   202     case 0: 
   203         memcpy( tmp, region, size );
   204         for( i=0; i<run_count; i++ ) {
   205             memcpy( buf, tmp, size );
   206             buf += size;
   207         }
   208         break;
   209     case 1: 
   210         i = run_count * size;
   211         memcpy( buf, region, i );
   212         source += i;
   213         break;
   214     case 2: 
   215         for( i=0; i<run_count; i++ ) {
   216             memcpy( buf, region, size );
   217             buf += size;
   218             region -= size;
   219         }
   220         source -= (run_count * size);
   221         break;
   222     default:
   223         return 0; /* Illegal */
   224     }
   226     /* Update the channel registers */
   227     count -= run_count;
   228     MMIO_WRITE( DMAC, SAR0 + (channel<<4), source );
   229     MMIO_WRITE( DMAC, DMATCR0 + (channel<<4), count );
   230     if( count == 0 ) {
   231         control |= CHCR_TE; 
   232         if( IS_CHANNEL_IRQ_ENABLED(control) )
   233             intc_raise_interrupt( INT_DMA_DMTE0 + channel );
   234         MMIO_WRITE( DMAC, CHCR0 + (channel<<4), control );
   235     }
   237     return run_count * size;
   238 }
   240 uint32_t DMAC_put_buffer( int channel, sh4ptr_t buf, uint32_t numBytes )
   241 {
   242     uint32_t control = DMA_CONTROL(channel);
   243     uint32_t dest, count, run_count, size, i;
   245     if( !IS_CHANNEL_ENABLED(control) || !IS_DMAC_ENABLED() )
   246         return 0;
   248     if( ((control >> 8) & 0x0F) !=  DMARES_DEVICE_TO_MEMORY ) {
   249         /* Error? */
   250         return 0;
   251     } 
   253     dest = DMA_DEST(channel);
   254     count = DMA_COUNT(channel);
   255     if( count == 0 ) count = 0x01000000;
   257     size = DMAC_xfer_size[ (control >> 4)&0x07 ];
   258     run_count = numBytes / size;
   259     if( run_count > count || run_count == 0 )
   260         run_count = count;
   262     /* Do copy - FIXME: doesn't work when crossing regions */
   263     sh4ptr_t region = mem_get_region( dest );
   264     switch( (control >> 12) & 0x03 ) {
   265     case 0: 
   266         for( i=0; i<run_count; i++ ) { 
   267             /* Doesn't make a whole lot of sense, but hey... */
   268             memcpy( region, buf, size );
   269             buf += size;
   270         }
   271         break;
   272     case 1: 
   273         i = run_count * size;
   274         memcpy( region, buf, i );
   275         dest += i;
   276         break;
   277     case 2: 
   278         for( i=0; i<run_count; i++ ) {
   279             memcpy( region, buf, size );
   280             buf += size;
   281             region -= size;
   282         }
   283         dest -= (run_count * size);
   284         break;
   285     default:
   286         return 0; /* Illegal */
   287     }
   289     /* Update the channel registers */
   290     count -= run_count;
   291     MMIO_WRITE( DMAC, DAR0 + (channel<<4), dest );
   292     MMIO_WRITE( DMAC, DMATCR0 + (channel<<4), count );
   293     if( count == 0 ) {
   294         control |= CHCR_TE; 
   295         if( IS_CHANNEL_IRQ_ENABLED(control) )
   296             intc_raise_interrupt( INT_DMA_DMTE0 + channel );
   297         MMIO_WRITE( DMAC, CHCR0 + (channel<<4), control );
   298     }
   299     return run_count * size;
   300 }
   302 void DMAC_reset( void )
   303 {
   305 }
   307 void DMAC_save_state( FILE *F ) 
   308 {
   310 }
   312 int DMAC_load_state( FILE *f )
   313 {
   314     return 0;
   315 }
   317 void DMAC_trigger( int resource )
   318 {
   319     int i;
   320     if( !IS_DMAC_ENABLED() )
   321         return;
   322     for( i=0; i<4; i++ ) {
   323         uint32_t control = DMA_CONTROL(i);
   324         if( IS_CHANNEL_ENABLED(control) ) {
   325             uint32_t channel_res = CHANNEL_RESOURCE(control);
   326             switch( resource ) {
   327             case DMAC_EXTERNAL:
   328                 if( channel_res == DMARES_MEMORY_TO_MEMORY )
   329                     DMAC_run_channel(i,1);
   330                 break;
   331             case DMAC_SCI_TDE:
   332                 if( channel_res == DMARES_SCI_TRANSMIT_EMPTY )
   333                     DMAC_run_channel(i,1);
   334                 break;
   335             case DMAC_SCI_RDF:
   336                 if( channel_res == DMARES_SCI_RECEIVE_FULL )
   337                     DMAC_run_channel(i,1);
   338                 break;
   339             case DMAC_SCIF_TDE:
   340                 if( channel_res == DMARES_SCIF_TRANSMIT_EMPTY )
   341                     DMAC_run_channel(i,1);
   342                 break;
   343             case DMAC_SCIF_RDF:
   344                 if( channel_res == DMARES_SCIF_RECEIVE_FULL )
   345                     DMAC_run_channel(i,1);
   346                 break;
   347             case DMAC_TMU_ICI:
   348                 if( channel_res >= DMARES_MEMORY_TO_MEMORY_TMU ) 
   349                     DMAC_run_channel(i,1);
   350                 break;
   351             }
   352         }
   353     }
   354 }
.