mirror of https://github.com/contiki-ng/mspsim
244 lines
8.7 KiB
Java
244 lines
8.7 KiB
Java
package se.sics.mspsim.core;
|
|
|
|
import se.sics.mspsim.util.Utils;
|
|
|
|
public class DMA extends IOUnit {
|
|
|
|
/* global DMA configuration */
|
|
public static final int DMACTL0 = 0x122;
|
|
public static final int DMACTL1 = 0x124;
|
|
|
|
/* per channel configuration */
|
|
public static final int DMAxCTL = 0x1e0;
|
|
public static final int DMAxSA = 0x1e2;
|
|
public static final int DMAxDA = 0x1e4;
|
|
public static final int DMAxSZ = 0x1e6;
|
|
|
|
/* DMA TSELx - from msp430x1xxx devices */
|
|
/* new devices has more channels and more triggers */
|
|
public static final int DMAREQ = 0;
|
|
public static final int TACCR2 = 1;
|
|
public static final int TBCCR2 = 2;
|
|
public static final int URXIFG0 = 3; /* UART 0 */
|
|
public static final int UTXIFG0 = 4; /* UART 0 */
|
|
public static final int DAC12_0 = 5;
|
|
public static final int ADC12_0 = 6;
|
|
public static final int TACCR0 = 7;
|
|
public static final int TBCCR0 = 8;
|
|
public static final int URXIFG1 = 9; /* UART 1 */
|
|
public static final int UTXIFG1 = 10; /* UART 1 */
|
|
public static final int MULTIPLIER = 11;
|
|
|
|
public static final int IFG_MASK = 0x08;
|
|
|
|
private static final int[] INCR = {0,0,-1,1};
|
|
|
|
private InterruptMultiplexer interruptMultiplexer;
|
|
|
|
class Channel implements InterruptHandler {
|
|
int channelNo;
|
|
/* public registers */
|
|
|
|
int ctl;
|
|
int sourceAddress;
|
|
int destinationAddress;
|
|
int size;
|
|
|
|
/* internal registers */
|
|
int currentSourceAddress;
|
|
int currentDestinationAddress;
|
|
int storedSize;
|
|
|
|
int srcIncr = 0;
|
|
int dstIncr = 0;
|
|
boolean dstByteMode = false;
|
|
boolean srcByteMode = false;
|
|
|
|
DMATrigger trigger;
|
|
int triggerIndex;
|
|
int transferMode = 0;
|
|
|
|
boolean enable = false;
|
|
boolean dmaLevel = false; /* edge or level sensitive trigger */
|
|
boolean dmaIE = false;
|
|
boolean dmaIFG = false;
|
|
|
|
public Channel(int i) {
|
|
channelNo = i;
|
|
}
|
|
|
|
public void setTrigger(DMATrigger t, int index) {
|
|
System.out.println("Setting trigger to " + t);
|
|
trigger = t;
|
|
triggerIndex = index;
|
|
}
|
|
|
|
public void write(int address, int data) {
|
|
switch(address) {
|
|
case 0:
|
|
ctl = data;
|
|
transferMode = (data >> 12) & 7;
|
|
dstIncr = INCR[(data >> 10) & 3];
|
|
srcIncr = INCR[(data >> 8) & 3];
|
|
dstByteMode = (data & 0x80) > 0; /* bit 7 */
|
|
srcByteMode = (data & 0x40) > 0; /* bit 6 */
|
|
dmaLevel = (data & 0x20) > 0; /* bit 5 */
|
|
boolean enabling = !enable && (data & 0x10) > 0;
|
|
enable = (data & 0x10) > 0; /* bit 4 */
|
|
dmaIFG = (data & IFG_MASK) > 0; /* bit 3 */
|
|
dmaIE = (data & 0x04) > 0; /* bit 2 */
|
|
System.out.println("DMA Ch." + channelNo + ": config srcIncr: " + srcIncr + " dstIncr:" + dstIncr
|
|
+ " en: " + enable + " srcB:" + srcByteMode + " dstB:" + dstByteMode + " level: " + dmaLevel +
|
|
" transferMode: " + transferMode + " ie:" + dmaIE);
|
|
/* this might be wrong ? */
|
|
if (enabling) trigger(trigger, triggerIndex);
|
|
interruptMultiplexer.updateInterrupt(dmaIFG & dmaIE, channelNo);
|
|
break;
|
|
case 2:
|
|
sourceAddress = data;
|
|
currentSourceAddress = data;
|
|
break;
|
|
case 4:
|
|
destinationAddress = data;
|
|
currentDestinationAddress = data;
|
|
break;
|
|
case 6:
|
|
size = data;
|
|
storedSize = data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
public int read(int address) {
|
|
switch(address) {
|
|
case 0:
|
|
/* set the IFG */
|
|
ctl = (ctl & ~IFG_MASK) | (dmaIFG ? IFG_MASK : 0);
|
|
return ctl;
|
|
case 2:
|
|
return sourceAddress;
|
|
case 4:
|
|
return destinationAddress;
|
|
case 6:
|
|
return size;
|
|
}
|
|
System.out.println("Illegal read of DMA Channel register");
|
|
return 0;
|
|
}
|
|
|
|
public void trigger(DMATrigger trigger, int index) {
|
|
/* perform memory move and possibly clear triggering flag!!! */
|
|
/* NOTE: show config byte/word also !!! */
|
|
if (enable) {
|
|
int data = cpu.read(currentSourceAddress, false);
|
|
System.out.println("DMA Triggered reading from: " +
|
|
currentSourceAddress + " => " + data + " " + (char) data +
|
|
" size:" + size + " index:" + index);
|
|
trigger.clearDMATrigger(index);
|
|
DMA.this.cpu.write(currentDestinationAddress, data, false);
|
|
|
|
currentSourceAddress += srcIncr;
|
|
currentDestinationAddress += dstIncr;
|
|
size--;
|
|
if (size == 0) {
|
|
currentSourceAddress = sourceAddress;
|
|
currentDestinationAddress = destinationAddress;
|
|
size = storedSize;
|
|
if ((transferMode & 0x04) == 0) {
|
|
enable = false;
|
|
}
|
|
/* flag interrupt and update interrupt vector */
|
|
dmaIFG = true;
|
|
interruptMultiplexer.updateInterrupt(dmaIFG & dmaIE, channelNo);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void interruptServiced(int vector) {
|
|
}
|
|
|
|
public String getName() {
|
|
return "DMA Channel " + channelNo;
|
|
}
|
|
}
|
|
|
|
private Channel channels[] = new Channel[3];
|
|
private int dmactl0;
|
|
private int dmactl1;
|
|
|
|
MSP430Core cpu;
|
|
|
|
/* MAX 16 triggers ? */
|
|
private DMATrigger[] dmaTrigger = new DMATrigger[16];
|
|
private int[] dmaTriggerIndex = new int[16];
|
|
|
|
public DMA(String id, int[] memory, int offset, MSP430Core msp430Core) {
|
|
super(id, memory, offset);
|
|
channels[0] = new Channel(0);
|
|
channels[1] = new Channel(1);
|
|
channels[2] = new Channel(2);
|
|
this.cpu = msp430Core;
|
|
}
|
|
|
|
public void setInterruptMultiplexer(InterruptMultiplexer interruptMultiplexer) {
|
|
this.interruptMultiplexer = interruptMultiplexer;
|
|
}
|
|
|
|
public void setDMATrigger(int totindex, DMATrigger trigger, int tIndex) {
|
|
dmaTrigger[totindex] = trigger;
|
|
dmaTriggerIndex[totindex] = tIndex;
|
|
trigger.setDMA(this);
|
|
}
|
|
|
|
public void trigger(DMATrigger trigger, int index) {
|
|
/* could make this a bit and have a bit-pattern if more dma channels but
|
|
* with 3 channels it does not make sense. Optimize later - maybe with
|
|
* flag in DMA triggers so that they now if a channel listens at all.
|
|
*/
|
|
for (int i = 0; i < channels.length; i++) {
|
|
// System.out.println("DMA Channel:" + i + " " + channels[i].trigger + " = " + trigger);
|
|
if (channels[i].trigger == trigger &&
|
|
channels[i].triggerIndex == index) channels[i].trigger(trigger, index);
|
|
}
|
|
}
|
|
|
|
public void interruptServiced(int vector) {
|
|
}
|
|
|
|
public void write(int address, int value, boolean word, long cycles) {
|
|
System.out.println("DMA write to: " + Utils.hex16(address) + ": " + value);
|
|
switch (address) {
|
|
case DMACTL0:
|
|
/* DMA Control 0 */
|
|
dmactl0 = value;
|
|
channels[0].setTrigger(dmaTrigger[value & 0xf], dmaTriggerIndex[value & 0xf]);
|
|
channels[1].setTrigger(dmaTrigger[(value >> 4) & 0xf], dmaTriggerIndex[(value >> 4) & 0xf]);
|
|
channels[2].setTrigger(dmaTrigger[(value >> 8) & 0xf], dmaTriggerIndex[(value >> 8) & 0xf]);
|
|
break;
|
|
case DMACTL1:
|
|
/* DMA Control 1 */
|
|
dmactl1 = value;
|
|
break;
|
|
default:
|
|
/* must be word ??? */
|
|
Channel c = channels[(address - DMAxCTL) / 8];
|
|
c.write(address & 0x07, value);
|
|
}
|
|
}
|
|
|
|
public int read(int address, boolean word, long cycles) {
|
|
switch (address) {
|
|
case DMACTL0:
|
|
/* DMA Control 0 */
|
|
return dmactl0;
|
|
case DMACTL1:
|
|
/* DMA Control 1 */
|
|
return dmactl1;
|
|
default:
|
|
/* must be word ??? */
|
|
Channel c = channels[(address - DMAxCTL) / 8];
|
|
return c.read(address & 7);
|
|
}
|
|
}
|
|
}
|