⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dma.c

📁 一个用在mips体系结构中的操作系统
💻 C
字号:
/* * Copyright (C) 1996-1998 by the Board of Trustees *    of Leland Stanford Junior University. *  * This file is part of the SimOS distribution.  * See LICENSE file for terms of the license.  * *//***************************************************************** * dma.c *  * Author: $Author: bosch $ * Date: $Date: 1998/02/10 00:36:17 $ *****************************************************************/#include <stdio.h>#include <stdlib.h>#include <string.h>#include "sim.h"#include "simtypes.h"#include "cpu_interface.h"#include "eventcallback.h" #include "dma.h"#include "assert.h"#include "sim_error.h"#include "simutil.h"#include "machine_params.h"#include "../../caches/memref.h"#include "arch_specifics.h"typedef struct DMAChannel {    EventCallbackHdr evthdr;   /* So we can make this an event callback */   bool busy;                 /* DMA channel in use. */   int  cpuNum;               /* CPU doing DMA */   int execCpuNum;            /* CPU executing the callbacks and defining                                * the timing */   DMARequest *req;   void (*doneRoutine)(int);  /* Routine to call when done */   int doneArg;               /* Argument for done routine */   SimTime finishTime;   int  cyclesPerLine;        /* Rate per cache line */      SimTime startTime;         /* Time to start next cache line DMA */   int  currentOffset;        /* Current byte offset into DMA */   bool inMemorySystem;       /* True if DMA is in memory system */   int  lineSize;             /* L2-cache line size when defined */   int transId;} DMAChannel;static DMAChannel  *dmaChannel;static void DMACallBack( int cpuNum,EventCallbackHdr *ptr,void *arg);static void DMAPrime(DMAChannel *, DMAStatus status);/*  * Initialize all of the DMA channels */voidDMAInit(void){   int i;   static int initialized = 0;   ASSERT( !initialized );   initialized = 1;   dmaChannel = (DMAChannel *)       ZMALLOC(sizeof(DMAChannel)*NUM_DMA_CHANNELS, "DmaChannel");   for (i = 0; i < NUM_DMA_CHANNELS; i++) {      dmaChannel[i].busy = FALSE;   }}/* * DMAdoTransfer - Perform the specified DMA operation.  */void DMAdoTransfer(int cpuNum, DMARequest *req, SimTime finishTime,                void (*done)(int), int doneArg, int execCpuNum){   int i;   SimTime now = (CPUVec.CycleCount ? CPUVec.CycleCount(execCpuNum):0);   ASSERT(finishTime >= now);   for (i = 0; i < NUM_DMA_CHANNELS; i++) {	if (!dmaChannel[i].busy) {          break;        }   }   if (i >= NUM_DMA_CHANNELS) {       CPUError("Out of DMA channels\n");   }   SIM_DEBUG(('d', "DMA cpu=%i isDMAWrite=%i addr=0x%x+0x%x len=%i\n",              cpuNum,req->isDMAWrite,*req->pAddrs,              req->offset, req->remainingLen));   ASSERT( req->remainingLen > 0  );   ASSERT( req->handler != 0 );   dmaChannel[i].transId = DMACHANNEL2TRANSID(i);   dmaChannel[i].req = req;   dmaChannel[i].req->dmaLen = 0; /* Set to zero as nothing has been transferred yet */   dmaChannel[i].busy = TRUE;   dmaChannel[i].cpuNum = cpuNum;   dmaChannel[i].execCpuNum = execCpuNum;   dmaChannel[i].doneRoutine = done;   dmaChannel[i].doneArg = doneArg;   dmaChannel[i].cyclesPerLine = (finishTime -now) *                                   SCACHE_LINE_SIZE / req->remainingLen;   dmaChannel[i].finishTime = finishTime;   dmaChannel[i].currentOffset = 0;   dmaChannel[i].startTime = now;   dmaChannel[i].inMemorySystem = FALSE;   /*     * The HP disk model apparently can specify a finish time smaller than    * the current time.    */   if( dmaChannel[i].startTime > dmaChannel[i].finishTime ) {       dmaChannel[i].finishTime = dmaChannel[i].startTime;   }   dmaChannel[i].lineSize = ( SCACHE_LINE_SIZE ? SCACHE_LINE_SIZE : 256 );   DMAPrime(&dmaChannel[i], DMA_OK); }static Result NullMemsysDMARead(int cpuNum, PA paddr, int transId, int length, byte *data){   char *memAddr = PHYS_TO_MEMADDR(M_FROM_CPU(cpuNum), paddr);   SIM_DEBUG(('d', "DMA DATA cpu %d READ pAddr=%x len=%i data=%x %x %x %x\n",              cpuNum, paddr,length, ((uint *)data)[0],              ((uint *)data)[1],((uint *)data)[2],((uint *)data)[3]));      bcopy(memAddr, data, length);   DMACmdDone(transId, DMA_OK);   return SUCCESS;}static Result NullMemsysDMAWrite(int cpuNum, PA paddr, int transId, int length, byte *data){   char *memAddr = PHYS_TO_MEMADDR(M_FROM_CPU(cpuNum), paddr);      SIM_DEBUG(('d', "DMA DATA cpu %d WRITE pAddr=%x len=%i data=%x %x %x %x\n",              cpuNum, paddr, length, ((uint *)data)[0],              ((uint *)data)[1],((uint *)data)[2],((uint *)data)[3]));   #ifndef SOLO   if (CPUVec.CheckFirewall && !CPUVec.CheckFirewall(cpuNum, paddr)) {      return BUSERROR;   }#endif   bcopy(data, memAddr, length);      ASSERT( data );      DMACmdDone(transId, DMA_OK);   return SUCCESS;}/***************************************************************** * Prime DMA to start or continue the current request. * execCpuNum is the cpuNum where the callback take place (where this code * executes. When using the HP disk model, this is always CPU 0. *****************************************************************/static voidDMAPrime(DMAChannel *dma, DMAStatus status){   SimTime cycleCount;   Result ret;   ASSERT(dma->busy);   cycleCount = (CPUVec.CycleCount ?                  CPUVec.CycleCount(dma->execCpuNum) : SIM_MAX_TIME );      if (status == DMA_NAK) {      /* try again */      dma->req->dmaLen = 0;   } else if (status != DMA_OK) {      /* yowsa, memsystem didn't like the last access!  This       * can happen if the OS tries to DMA to a page that is       * firewall-protected against it, or if the memory system       * NAKs the request for some reason.       *       * Right thing to do is retry on a NAK and abort transfer       * on an error.  For now just let the user know that something       * has gone wrong.       */      SIM_DEBUG_DETAIL(('d', "DMA-fail", dma->cpuNum,                        "DMA %s attempt to %x rejected\n",                        dma->req->isDMAWrite ? "write" : "read",                         (PA)(*(dma->req->pAddrs) + dma->req->offset)));      CPUWarning("DMA failure -- not handled properly yet by DMAPrime -- see cpu.log\n");   }      while (1) {      int lastDMALen = dma->req->dmaLen;            /*       * Timing: wait for end.        */      if (dma->startTime > cycleCount ) {         SimTime time = dma->startTime - cycleCount;         /* Request's time hasn't occurred yet, delay until it occurs. */         EventDoCallback(dma->execCpuNum, DMACallBack, &dma->evthdr, dma, time);         return;      }             /*       * figure out next DMA line       */      ASSERT(lastDMALen >= 0 && lastDMALen <= dma->lineSize);      dma->req->offset       += lastDMALen;      dma->req->remainingLen -= lastDMALen;      dma->req->amountMoved  += lastDMALen;            /*       * Go over page boundary. Adjust offset       */      if( dma->req->offset > PAGE_SIZE ) {          CPUError("dma.c:  %i > %i \n", dma->req->offset, PAGE_SIZE);      }               ASSERT( dma->req->offset <= PAGE_SIZE );            if (dma->req->offset == PAGE_SIZE) {          dma->req->offset = 0;         dma->req->pAddrs++;         ASSERT( !dma->req->remainingLen || *dma->req->pAddrs );      }                if (dma->req->remainingLen) {         int   dmalen;         PA    pAddr = (PA)(*(dma->req->pAddrs) + dma->req->offset);                  if( (dma->req->offset & (dma->lineSize-1)) != 0 ) {            dmalen = dma->req->offset & (dma->lineSize-1);            dmalen = dma->lineSize - dmalen;                        if (dmalen > dma->req->remainingLen) {               dmalen = dma->req->remainingLen;            }                        } else {             if( dma->req->remainingLen > dma->lineSize ) {                dmalen = dma->lineSize;            } else {                dmalen = dma->req->remainingLen;            }         }                  ASSERT(dmalen > 0 && dmalen <= dma->lineSize);         dma->req->dmaLen        = dmalen;                  /* if this is a DMA write request, must update the data          * buffer right away.          */         if ((dma->req->isDMAWrite) && (status != DMA_NAK)) {            dma->req->handler(dma->req);            ASSERT(dma->req->data != 0);         }                  /*          * Call into memory system. Two possible outcomes:          *  - SUCCESS: transfer has taken place          *  - STALL: you're stalled, expect a callback to DMACmdDone when          *    the transfer has finished.          */         dma->startTime += dma->cyclesPerLine;                    dma->inMemorySystem = 1;                           if (CPUVec.useMemRef) {            if (dma->req->isDMAWrite) {               ret = MemRefDMAWrite(dma->cpuNum, pAddr, dma->transId, dmalen,                                     dma->req->data);            } else {               ret = MemRefDMARead(dma->cpuNum, pAddr, dma->transId, dmalen,                                    dma->req->data);            }         } else {            if (dma->req->isDMAWrite) {               ret = NullMemsysDMAWrite(dma->cpuNum, pAddr, dma->transId,                                        dmalen, dma->req->data);            } else {               ret = NullMemsysDMARead(dma->cpuNum, pAddr, dma->transId,                                       dmalen, dma->req->data);            }         }         dma->inMemorySystem = 0;         if (ret != SUCCESS) {            /* wait for callback to DMACmdDone */            break;          }      } else {         /* Finished with DMA or no memory system (embra)           * Eventually wait until desired minimal end time.           */         if (CPUVec.CycleCount && cycleCount < dma->finishTime) {             dma->req->dmaLen = 0;            EventDoCallback(dma->execCpuNum,DMACallBack,&dma->evthdr,                             dma, dma->finishTime - cycleCount);            return;         } else {             dma->busy = FALSE;            dma->doneRoutine(dma->doneArg);            return;         }      }   }      if (ret == BUSERROR) {      /* this will only happen with 0-latency mem systems,       * eg. embra.  Interestingly, we'll call right around       * to the top of this function to detect the error...       * let's do it this way for consistency with other       * memory systems rather than just doing a goto.       */      DMACmdDone(dma->transId, DMA_BAD);      return;   }   ASSERT(ret == STALL);   return;                /* wait for callback to DMACmdDone */}/* * DMACmdDone - Called when a DMA memory system request finishes. */void DMACmdDone(int transId, DMAStatus status){   DMAChannel* dma = &dmaChannel[TRANSID2DMACHANNEL(transId)];   ASSERT(dma->busy);      /* need to call the data handling function on DMA reads to    * squirrel away the data and prepare for the next transfer.    */   if ((!dma->req->isDMAWrite) && (status != DMA_NAK)) {      ASSERT(dma->req->dmaLen > 0);      dma->req->handler(dma->req);   }      /* prime DMA for rest of transfer (if any) */   /* Don't call DMAPrime if we're already in the memory system --    * the DMAPrime while loop will initiate the next request.    * This only happens on zero-latency operations. */   if (!dma->inMemorySystem) {      DMAPrime(dma, status);   }}static void DMACallBack(int cpuNum, EventCallbackHdr *ptr,void *arg){   DMAChannel *dma = (DMAChannel*)arg;   DMAPrime(dma, DMA_OK);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -