code: /**
   * Created on 15. Mai 2005, 15:00
   *
   * Copyright (c) 2005, Olaf Landsiedel, Thomas Gaertner, Protocol Engineering and
   * Distributed Systems, University of Tuebingen
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * Redistributions of source code must retain the above copyright notice,
   * this list of conditions and the following disclaimer.
   *
   * Redistributions in binary form must reproduce the above copyright
   * notice, this list of conditions and the following disclaimer in the
   * documentation and/or other materials provided with the distribution.
   *
   * Neither the name of the Protocol Engineering and Distributed Systems
   * Group, the name of the University of Tuebingen nor the names of its
   * contributors may be used to endorse or promote products derived from
   * this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  package avrora.sim.platform;
  
  import avrora.sim.FiniteStateMachine;
  import avrora.sim.Simulator;
  import avrora.sim.output.SimPrinter;
  import avrora.sim.state.BooleanView;
  import avrora.sim.state.BooleanView.ValueSetListener;
  import avrora.sim.state.Register;
  import avrora.sim.state.RegisterUtil;
  import avrora.sim.clock.Clock;
  import avrora.sim.energy.Energy;
  import avrora.sim.mcu.Microcontroller;
  import cck.text.Terminal;
  
  /**
   * The <code>ExternalFlash</code> class implements the necessary functionality of the
   * Atmega Dataflash interface to use the Mica2 DataFlash
   * This device requires use of the following pins:
   * <p/>
   * PA3 - Flash Cable Seclect
   * <p/>
   * PD2 - USART1_RXD
   * PD3 - USART1_TXD
   * PD5 - USART1_CLK
   *
   * @author Thomas Gaertner
   */
  public class ExternalFlash {
  
      protected final Simulator sim;
      protected final Clock clock;
      protected Microcontroller mcu;
      protected final SimPrinter printer;
      private boolean isSelected;	// true if PA3 is 0
      private boolean isReading;		// mcu is reading from so of dataflash?
      private int dfOpcode;
      private int dfPageAddress;
      private int dfByteOffset;
      private int dfTempByte;
      private short dfStatus;		// Dataflash Status Register
      private double delay;		// delay while busy in ms
      private BooleanView so = RegisterUtil.booleanView(new Register(1), 0); // serial output 
      private boolean si;		// serial output, serial input
      private int icPage;		    // internal address counter
      private boolean tick;
      private short step;
      private byte i;
  
      //	DataFlash Status Register
      //	bits 5, 4, 3
      public static final int DF_STATUS_REGISTER_DENSITY = 0x18;
      public static final int DF_STATUS_READY = 0x80;
      public static final int DF_STATUS_COMPARE = 0x40;
      //	SC Characteristics
      //	all below in ms
      public static final int DF_TEP = 20;
      public static final int DF_TP = 14;
      public static final int DF_TPE = 8;
      public static final int DF_TBE = 12;
      public static final double DF_TXFR = 0.0003;
  
      // names of the states of this device
      private static final String[] modeName = {"standby", "read", "write", "load"};
      // power consumption of the device states
      private static final double[] modeAmpere = {0.000002, 0.004, 0.015, 0.000002};
      // default mode of the device is standby
      private static final int startMode = 0;
      // the Dataflash Memory
      public Memory memory;
  
      protected final FiniteStateMachine stateMachine;
  
      /**
       * The <code>Memory</code> class simulates the Dataflash Memory
       */
      private class Memory {
          private final int bytesPerPage;
          private final Page[] pages;
          Page buffer1;
          Page buffer2;
  
          protected Memory(int numPages, int numBytes) {
              pages = new Page[numPages];
              bytesPerPage = numBytes;
              buffer1 = new Page(numBytes);
              buffer2 = new Page(numBytes);
          }
  
          Page getPage(int pageNum) {
              ExternalFlash.Page page = pages[pageNum];
              if (page == null) {
                  page = new Page(bytesPerPage);
                  pages[pageNum] = page;
              }
              return page;
          }
  
          void setPage(int pageNum, Page page) {
              pages[pageNum] = page;
          }
      }
  
      private class Page {
          final short[] bytes;
  
          protected Page(int numBytes) {
              bytes = new short[numBytes];
              java.util.Arrays.fill(bytes, (short)0xff);  // empty flash is filled with 0xff, not 0!
          }
          void debug() {
              if (printer != null) {
                  int i;
                  for(i = 0; i < bytes.length; i++) {
                      echo("Byte " + i + " = " + bytes[i]);
                  }
              }
          }
      }
  
      public ExternalFlash(Microcontroller mcunit, int numPages, int pageBytes) {
          memory = new Memory(numPages, pageBytes);
          mcu = mcunit;
          sim = mcu.getSimulator();
          printer = sim.getPrinter("mica2.flash");
          clock = sim.getClock();
          dfStatus = DF_STATUS_REGISTER_DENSITY | DF_STATUS_READY;
          tick = false;
          i = 0;
          step = 0;
  
          stateMachine = new FiniteStateMachine(clock, startMode, modeName, 0);
          // connect Pins
          // output
          mcu.getPin("PA3").connectOutput(new PA3Output());
          mcu.getPin("PD3").connectOutput(new PD3Output());
          mcu.getPin("PD5").connectOutput(new PD5Output());
          // input
          mcu.getPin("PD2").connectInput(new PD2Input());
  
          //setup energy recording
          new Energy("flash", modeAmpere, stateMachine, sim.getEnergyControl());
      }
  
      private Page getMemoryPage(int num) {
          return memory.getPage(num);
      }
  
      private short getMemoryPageAt(int num, int offset) {
          return memory.getPage(num).bytes[offset];
      }
  
      private void setMemoryPage(int num, Page val) {
          memory.setPage(num, val);
          val.debug();
      }
  
      private Page getBuffer1() {
          return memory.buffer1;
      }
  
      private short getBuffer1(int offset) {
          return memory.buffer1.bytes[offset];
      }
  
      private void setBuffer1(Page value) {
          memory.buffer1 = value;
      }
  
      private void setBuffer1(int offset, short value) {
          memory.buffer1.bytes[offset] = value;
      }
  
      private Page getBuffer2() {
          return memory.buffer2;
      }
  
      private short getBuffer2(int offset) {
          return memory.buffer2.bytes[offset];
      }
  
      private void setBuffer2(Page value) {
          memory.buffer2 = value;
      }
  
      private void setBuffer2(int offset, short value) {
          memory.buffer2.bytes[offset] = value;
      }
  
      private void copyBuffer1toPage(int num) {
          setMemoryPage(num, getBuffer1());
      }
  
      private void copyBuffer2toPage(int num) {
          setMemoryPage(num, getBuffer2());
      }
  
      private void copyPageToBuffer1(int num) {
          setBuffer1(getMemoryPage(num));
      }
  
      private void copyPageToBuffer2(int num) {
          setBuffer2(getMemoryPage(num));
      }
  
      // Flash_CS as output pin
      protected class PA3Output implements Microcontroller.Pin.Output {
          // Flash_CS is connected inverted
          public void write(boolean level) {
              if (!level && !isSelected) {
                  // falling edge, so instruction starts
                  if (clock.getCount() > 1500) {
                      echo("Instruction started");
                  }
                  isSelected = true;
              } else if (level && isSelected) {
                  // rising edge, so instruction terminates
                  if (clock.getCount() < 1500) {
                      echo("initialized");
                  } else {
                      echo("Instruction finished");
                  }
                  isSelected = false;
  
                  switch (dfOpcode) {
                      // Read Commands
                      case 0x68:  // Continous Array Read
                      case 0xE8:  // Continous Array Read
                      case 0x52:  // Main Memory Page Read
                      case 0xD2:  // Main Memory Page Read
                      case 0x54:  // Buffer1 Read
                      case 0xD4:  // Buffer1 Read
                      case 0x56:  // Buffer2 Read
                      case 0xD6:  // Buffer2 Read
                      case 0x57:  // Status Register Read
                      case 0xD7:  // Status Register Read
                          break;
  
                          // Program and Erase Commands
                      case 0x83:  // Buffer1 to Memory with Built-in Erase
                          copyBuffer1toPage(dfPageAddress);
                          echo("copy Buffer1 to Memory Page " + dfPageAddress);
                          delay = DF_TEP;
                          break;
  
                      case 0x86:  // Buffer2 to Memory with Built-in Erase
                          copyBuffer2toPage(dfPageAddress);
                          echo("copy Buffer2 to Memory Page " + dfPageAddress);
                          delay = DF_TEP;
                          break;
  
                      case 0x88:  // Buffer1 to Memory without Built-in Erase
                          copyBuffer1toPage(dfPageAddress);
                          echo("copy Buffer1 to Memory Page " + dfPageAddress);
                          delay = DF_TP;
                          break;
  
                      case 0x89:  // Buffer2 to Memory without Built-in Erase
                          copyBuffer2toPage(dfPageAddress);
                          echo("copy Buffer2 to Memory Page " + dfPageAddress);
                          delay = DF_TP;
                          break;
  
                      case 0x81:  // Page Erase
                          delay = DF_TPE;
                          break;
  
                      case 0x50:  // Block Erase
                          delay = DF_TBE;
                          break;
  
                      case 0x82:  // Memory Program through Buffer1
                          // read from SI into Buffer1, write to Memory when Flash_CS gets 1
                          copyBuffer1toPage(dfPageAddress);
                          echo("copy Buffer1 to Memory Page " + dfPageAddress);
                          delay = DF_TEP;
                          break;
  
                      case 0x85:  // Memory Program through Buffer2
                          // read from SI into Buffer2, write to Memory when Flash_CS gets 1
                          copyBuffer2toPage(dfPageAddress);
                          echo("copy Buffer2 to Memory Page " + dfPageAddress);
                          delay = DF_TEP;
                          break;
  
                          // Additional Commands
                      case 0x53:  // Main Memory Page to Buffer1 Transfer
                          copyPageToBuffer1(dfPageAddress);
                          echo("copy Memory Page " + dfPageAddress + " to Buffer1");
                          delay = DF_TXFR;
                          break;
  
                      case 0x55:  // Main Memory Page to Buffer2 Transfer
                          copyPageToBuffer2(dfPageAddress);
                          echo("copy Memory Page " + dfPageAddress + " to Buffer2");
                          delay = DF_TXFR;
                          break;
  
                      case 0x60:  // Main Memory Page to Buffer1 Compare
                          if (getBuffer1() == getMemoryPage(dfPageAddress)) {
                              dfStatus &= ~DF_STATUS_COMPARE;
                              echo("compare Memory Page " + dfPageAddress + " to Buffer1: identical");
                          } else {
                              dfStatus |= DF_STATUS_COMPARE;
                              echo("compare Memory Page " + dfPageAddress + " to Buffer1: different");
                          }
                          delay = DF_TXFR;
                          break;
  
                      case 0x61:  // Main Memory Page to Buffer2 Compare
                          if (getBuffer2() == getMemoryPage(dfPageAddress)) {
                              dfStatus &= ~DF_STATUS_COMPARE;
                              echo("compare Memory Page " + dfPageAddress + " to Buffer2: identical");
                          } else {
                              dfStatus |= DF_STATUS_COMPARE;
                              echo("compare Memory Page " + dfPageAddress + " to Buffer2: different");
                          }
                          delay = DF_TXFR;
                          break;
  
                      case 0x58:  // Auto Page Rewrite
                      case 0x59:  // Auto Page Rewrite
                          delay = DF_TEP;
                          break;
  
                  }
  
                  // Dataflash is busy
                  dfStatus &= ~DF_STATUS_READY;
                  long dfDelay = clock.millisToCycles(delay / 1000);
                  clock.insertEvent(new Delay(), dfDelay);
  
                  // reset values
                  dfOpcode = 0;
                  dfByteOffset = 0;
                  dfPageAddress = 0;
                  step = 0;
                  isReading = false;
                  i = 0;
              }
          }
      }
  
      // USART1_TXD as output pin connected to SI
      protected class PD3Output implements Microcontroller.Pin.Output {
  
          public void write(boolean level) {
              si = level;
          }
      }
  
      // USART1_CLK as output pin connected to SCK
      protected class PD5Output implements Microcontroller.Pin.Output {
          private short temp;
  
          public void write(boolean level) {
              if (isSelected) {
                  // toggling SCK
                  if (tick != level) {
                      if (tick) {
  
                          // <<<<<<<< high-to-low <<<<<<<<
                          if (isReading) {
                              //echo("dfByteOffset = " + dfByteOffset);
                              //set so bitwise
                              setSO();
  
                              // Energy
                              stateMachine.transition(1); // read mode
  
                              i++;
  
                              if (i > 7) {
                                  echo("1 Byte of serial data was output on the SO: " + temp);
  
                                  // internal address counter
                                  int icOffset = dfByteOffset + 1;
                                  if (icOffset > 263) {
                                      icOffset -= 264;
                                      icPage = dfPageAddress++;
                                      if (icPage > 2047) {
                                          icPage -= 2048;
                                      }
                                  }
                                  dfByteOffset = icOffset;
                                  dfPageAddress = icPage;
                                  i = 0;
                              }
                          }
                      } else {
                          // >>>>>>>> low-to-high  >>>>>>>>
                          // first starts here with step 1: get opcode
                          if (!isReading) {
                              // get SI bytewise
                              dfTempByte |= (si ? 1 : 0) << (7 - i);  // MSB first
  
                              i++;
  
                              if (i > 7) {
                                  i = 0;
                                  step++;
                                  doStep();
                                  dfTempByte = 0;
  
                                  // energy
                                  if (step <= 4) {
                                      stateMachine.transition(3); // load
                                  } else {
                                      stateMachine.transition(2); // write
                                  }
                              }
                          }
                      }
                      // set clock state
                      tick = level;
                  }
              }
          }
  
          private void setSO() {
              switch (dfOpcode) {
                  case 0x68:  // Continous Array Read
                  case 0xE8:  // Continous Array Read
                  case 0x52:  // Main Memory Page Read
                  case 0xD2:  // Main Memory Page Read
  
                      // Buffer 1 Read
                  case 0x54:
                  case 0xD4:
                      temp = getBuffer1(dfByteOffset);
                      break;
  
                      // Buffer 2 Read
                  case 0x56:
                  case 0xD6:
                      temp = getBuffer2(dfByteOffset);
                      break;
  
                      // Status Register Read
                  case 0x57:
                  case 0xD7:
                      temp = dfStatus;
                      break;
  
                  default:
                      temp = getMemoryPageAt(dfPageAddress, dfByteOffset);
              }
  
              // write relevant bit to so
              so.setValue((temp & 1 << 7 - i) > 0); // MSB first
  
          }
  
          private void doStep() {
              switch (step) {
                  case 1:
                      //	get opcode
                      dfOpcode = dfTempByte;
                      echo("Received Opcode: " + dfOpcode);
                      // Status Register Read?
                      if (dfOpcode == 0x57 || dfOpcode == 0xD7) {
                          isReading = true;
                      }
                      break;
  
                  case 2:
                      // get first part of adressing sequence
                      dfPageAddress = dfTempByte << 7 & 0x0780;
                      echo("Received Address byte 1: " + dfTempByte);
                      break;
  
                  case 3:
                      // get second part of adressing sequence
                      dfPageAddress |= dfTempByte >> 1;
                      // and first part of byte offset
                      dfByteOffset = dfTempByte & 0x0100;
                      echo("Received Address byte 2: " + dfTempByte);
                      break;
  
                  case 4:
                      // get second part of byte offset
                      dfByteOffset |= dfTempByte;
                      echo("Received Address byte 3: " + dfByteOffset);
                      break;
  
                  default:
                      // adressing sequence complete
                      if (step > 4) {
                          doAction();
                      }
              }
          }
  
          private void doAction() {
              // ajust offset
              if (dfByteOffset > 263) {
                  dfByteOffset -= 264;
              }
              switch (dfOpcode) {
                  // Read Commands
  
                  // Continous Array Read
                  case 0x68:
                  case 0xE8:
                      // Additional Don't cares Required: 4 Bytes
                      if (step == 4 + 4) {
                          isReading = true;
                      }
                      break;
  
                      // Main Memory Page Read
                  case 0x52:
                  case 0xD2:
                      // Additional Don't cares Required: 4 Bytes
                      if (step == 4 + 4) {
                          isReading = true;
                      }
                      break;
  
                      // Buffer 1 Read
                  case 0x54:
                  case 0xD4:
                      // Additional Don't cares Required: 1 Byte
                      if (step == 4 + 1) {
                          isReading = true;
                      }
                      break;
  
                      // Buffer 2 Read
                  case 0x56:
                  case 0xD6:
                      if (step == 4 + 1) {
                          isReading = true;
                      }
                      break;
  
                      //	Status Register Read
                  case 0x57:
                  case 0xD7:
  
  
                      //Program and Erase Commands
  
                      //	Buffer 1 Write
                  case 0x84:
                      setBuffer1(dfByteOffset, (short) dfTempByte);
                      echo("written Buffer 1 Byte: " + (short)dfByteOffset + ": " + dfTempByte);
                      dfByteOffset += 1;
                      break;
  
                      //	Buffer 2 Write
                  case 0x87:
                      setBuffer2(dfByteOffset, (short) dfTempByte);
                      echo("written Buffer 2 Byte: " + (short)dfByteOffset + ": " + dfTempByte);
                      dfByteOffset += 1;
                      break;
  
                      // Buffer 1 to Memory with Built-in Erase
                  case 0x83:
                      // write when Flash_CS gets 1
                      break;
  
                      // Buffer 2 to Memory with Built-in Erase
                  case 0x86:
                      // write when Flash_CS gets 1
                      break;
  
                      // Buffer 1 to Memory without Built-in Erase
                  case 0x88:
                      // write when Flash_CS gets 1
                      break;
  
                      // Buffer 2 to Memory without Built-in Erase
                  case 0x89:
                      // write when Flash_CS gets 1
                      break;
  
                      // Page Erase
                  case 0x81:
                      // erase when Flash_CS gets 1
                      break;
  
                  case 0x50:  // Block Erase
                      // Block address in this case
                      dfPageAddress >>= 3;
                      break;
  
                      // Memory Program through Buffer 1
                  case 0x82:
                      // read from SI into buffer1, write to memory when Flash_CS gets 1
                      setBuffer1(dfByteOffset, (short) dfTempByte);
                      echo("written Buffer 1 Byte: " + (short)dfByteOffset + ": " + dfTempByte);
                      dfByteOffset += 1;
                      break;
  
                      // Memory Program through Buffer 2
                  case 0x85:
                      // read from SI into buffer2, write to mem when Flash_CS gets 1
                      setBuffer2(dfByteOffset, (short) dfTempByte);
                      echo("written Buffer 2 Byte: " + (short)dfByteOffset + ": " + dfTempByte);
                      dfByteOffset += 1;
                      break;
              }
          }
      }
  
      // Flash_RXD as input pin from SO
      protected class PD2Input extends Microcontroller.Pin.ListenableInput implements ValueSetListener {
          public PD2Input() {
              so.setValueSetListener(this);
          }
          
          // connected to serial output of dataflash
          public boolean read() {
              return so.getValue();
          }
          
          @Override
          public void onValueSet(BooleanView view, boolean newValue) {
              notifyListeners(newValue);
          }
      }
  
      protected class Delay implements Simulator.Event {
          /**
           * delay while dataflash is busy
           *
           * @see Simulator.Event#fire()
           */
          public void fire() {
              // operation finished
              dfStatus |= DF_STATUS_READY;
          }
      }
  
      private void echo(String str) {
          if (printer != null) {
              StringBuffer buf = printer.getBuffer(20);
              Terminal.append(Terminal.COLOR_BLUE, buf, "Dataflash");
              buf.append(": ");
              buf.append(str);
              printer.printBuffer(buf);
          }
      }
  
  }
  
  
  
  