📄 dma.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 + -