OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [or1ksim/] [peripheral/] [atadevice.c] - Rev 1712

Go to most recent revision | Compare with Previous | Blame | View Log

/*
    atadevice.c -- ATA Device simulation
    Simulates a harddisk, using a local streams.
    Copyright (C) 2002 Richard Herveille, rherveille@opencores.org
 
    This file is part of OpenRISC 1000 Architectural Simulator
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#include <string.h>
#include <stdarg.h>
 
#include "config.h"
 
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
 
#include "port.h"
#include "arch.h"
#include "abstract.h"
 
#include "atadevice.h"
#include "atacmd.h"
#include "sim-config.h"
#include "atadevice_cmdi.h"
 
#include "debug.h"
 
DEFAULT_DEBUG_CHANNEL(ata);
 
/*
 mandatory commands:
 - execute device diagnostics       (done)
 - flush cache
 - identify device                  (done)
 - initialize device parameters     (done)
 - read dma
 - read multiple
 - read sector(s)                   (done)
 - read verify sector(s)
 - seek
 - set features
 - set multiple mode
 - write dma
 - write multiple
 - write sector(s)
 
 
 optional commands:
 - download microcode
 - nop
 - read buffer
 - write buffer
 
 
 prohibited commands:
 - device reset
*/
 
 
/* Use a file to simulate a hard-disk                                 */
static FILE *open_file(uint32_t *size, const char *filename)
{
   FILE *fp;
   unsigned long  n;
 
   // TODO:
 
   /* check if a file with name 'filename' already exists             */
   if(!(fp = fopen(filename, "rb+")))
     if(!(fp = fopen(filename, "wb+"))) {
       ERR("ata_open_file, cannot open hd-file %s\n", filename);
       return NULL;
     } else {
       /* TODO create a file 'size' large */
       /* create a file 'size' large                                  */
       TRACE("ata_device; generating a new hard disk file.\n");
       TRACE("This may take a while, depending on the requested size.\n");
       for(n=0; n < *size; n++)
         fputc(0, fp);
     }
   else /* file already exist                                         */
     fprintf(stderr, "file %s already exists. Using existing file.\n", filename);
 
   TRACE("requested filesize was: %d (MBytes).\n", *size);
 
   /* get the size of the file. This is also the size of the harddisk*/
   fseek(fp, 0, SEEK_END);
   *size = ftell(fp);
 
   TRACE("actual filesize is: %d (MBytes).\n", *size >> 20);
 
   return fp;
}
 
 
/* Use a the local filesystem as a hard-disk                          */
static FILE *open_local(void)
{
  // TODO:
  FIXME("Device type LOCAL is not yet supported. Defaulting to device type NO_CONNECT.");
  return NULL;
}
 
static void ata_device_init(ata_device *device, int dev)
{
  /* set DeviceID                                                     */
  device->internals.dev = dev;
 
  /* generate stream for hd_simulation                                */
  switch(device->conf.type)
  {
    case TYPE_NO_CONNECT:
      TRACE("ata_device, using type NO_CONNECT.\n");
      device->conf.stream = NULL;
      break;
 
    case TYPE_FILE:
      TRACE("ata_device, using device type FILE.\n");
      device->conf.stream = open_file(&device->conf.size, device->conf.file);
      break;
 
    case TYPE_LOCAL:
      TRACE("ata_device, using device type LOCAL.\n");
      device->conf.stream = open_local();
      break;
 
    default:
      ERR("Illegal device-type.  Defaulting to type NO_CONNECT.\n");
      device->conf.stream = NULL;
      break;
  }
 
  device->conf.size_sect = device->conf.size / BYTES_PER_SECTOR;
}
 
/*
  D E V I C E S _ I N I T
*/
void ata_devices_init(ata_devices *devices)
{
  ata_device_init(&devices->device[0], 0);
 
  if(devices->device[0].conf.type)
    ata_device_init(&devices->device[1], ATA_DHR_DEV);
  else
    ata_device_init(&devices->device[1],           0);
}
 
/*
  D E V I C E S _ H W _ R E S E T
*/
static void ata_device_hw_reset(ata_device *device, int reset_signal,
  int daspo, int pdiagi, int daspi)
{
  /* check ata-device state                                         */
  if(device->internals.state == ATA_STATE_HW_RST) {
    if(!reset_signal) {
      /* hardware reset finished                                    */
 
      /* set sectors_per_track & heads_per_cylinders                */
      device->internals.sectors_per_track = device->conf.sectors;
      device->internals.heads_per_cylinder = device->conf.heads;
 
      /* set device1 input signals                                  */
      device->sigs.pdiagi = pdiagi;
      device->sigs.daspi  = daspi;
 
      ata_execute_device_diagnostics_cmd(device);
 
      /* clear busy bit                                             */
      device->regs.status &= ~ATA_SR_BSY;
 
      /* set DRDY bit, when not a PACKET device                     */
      if(!device->conf.packet)
        device->regs.status |= ATA_SR_DRDY;
 
      /* set new state                                              */
      device->internals.state = ATA_STATE_IDLE;
    }
  } else if (reset_signal) {
    /* hardware reset signal asserted, stop what we are doing     */
 
    /* negate bus signals                                         */
    device->sigs.iordy  = 0;
    device->sigs.intrq  = 0;
    device->sigs.dmarq  = 0;
    device->sigs.pdiago = 0;
    device->sigs.daspo  = daspo;
 
    /* set busy bit                                               */
    device->regs.status |= ATA_SR_BSY;
 
    /* set new state                                              */
    device->internals.state = ATA_STATE_HW_RST;
  }
}
 
/* power-on and hardware reset                                        */
void ata_devices_hw_reset(ata_devices *devices, int reset_signal)
{
  /* display debug information                                        */
  TRACE("ata_devices_hw_reset.\n");
 
  /* find device 0                                                    */
  if((devices->device[0].conf.stream) && (devices->device[1].conf.stream)) {
    /* this one is simple, device0 is device0                         */
 
    /* 1) handle device1 first                                        */
    ata_device_hw_reset(&devices->device[1], reset_signal,
			1,   /* assert dasp, this is device1          */
			0,   /* negate pdiag input, no more devices   */
			0);  /* negate dasp input, no more devices    */
 
    /* 2) Then handle device0                                         */
    ata_device_hw_reset(&devices->device[0], reset_signal,
			0,
			devices->device[1].sigs.pdiago,
			devices->device[1].sigs.daspo);
  } else if(devices->device[0].conf.stream) {
    /* device0 is device0, there's no device1                         */
    ata_device_hw_reset(&devices->device[0], reset_signal,
			0,   /* negate dasp, this is device0          */
			0,   /* negate pdiag input, there's no device1*/
			0);  /* negate dasp input, there's no device1 */
  } else if(devices->device[1].conf.stream) {
    /* device1 is (logical) device0, there's no (physical) device0    */
    ata_device_hw_reset(&devices->device[1], reset_signal,
			0,   /* negate dasp, this is device0          */
			0,   /* negate pdiag input, there's no device1*/
			0);  /* negate dasp input, there's no device1 */
  } else {
    /* no devices connected                                           */
    TRACE("ata_device_hw_reset, no devices connected.\n");
  }
}
 
 
/*
  D E V I C E S _ D O _ C O N T R O L _ R E G I S T E R
 
  Handles - software reset
          - Interrupt enable bit
*/
void ata_device_do_control_register(ata_device *device)
{
    /* TODO respond to new values of nIEN */
    /* if device not selected, respond to new values of nIEN & SRST */
 
  /* check if SRST bit is set                                         */
  if (device->regs.device_control & ATA_DCR_RST)
  {
    if (device->internals.state == ATA_STATE_IDLE)
    {   /* start software reset                                       */
        /* negate bus signals                                         */
        device->sigs.pdiago = 0;
        device->sigs.intrq  = 0;
        device->sigs.iordy  = 0;
        device->sigs.dmarq  = 0;
 
        /* set busy bit                                               */
        device->regs.status |= ATA_SR_BSY;
 
        /* set new state                                              */
        device->internals.state = ATA_STATE_SW_RST;
 
	/* display debug information                                  */
        TRACE("ata_device_sw_reset initiated.\n");
    }
  }
  else if (device->internals.state == ATA_STATE_SW_RST)
  {   /* are we doing a software reset ??                             */
      /* SRST bit cleared, end of software reset                      */
 
      /*execute device diagnostics                                    */
      ata_execute_device_diagnostics_cmd(device);
 
      /* clear busy bit                                               */
      device->regs.status &= ~ATA_SR_BSY;
 
      /* set DRDY bit, when not a PACKET device                       */
      if(!device->conf.packet)
        device->regs.status |= ATA_SR_DRDY;
 
      /* set new state                                                */
      device->internals.state = ATA_STATE_IDLE;
 
      /* display debug information                                    */
      TRACE("ata_device_sw_reset done.\n");
  }
  /*
  <else> We are doing a hardware reset (or similar)
         ignore command
  */
}
 
 
/*
  D E V I C E S _ D O _ C O M M A N D _ R E G I S T E R
*/
void ata_device_do_command_register(ata_device *device)
{
  /* check BSY & DRQ                                                  */
  if ( (device->regs.status & ATA_SR_BSY) || (device->regs.status & ATA_SR_DRQ) )
     if (device->regs.command != DEVICE_RESET)
        WARN("ata_device_write, writing a command while BSY or DRQ asserted.");
 
  /* check if device selected                                         */
  if ( (device->regs.device_head & ATA_DHR_DEV) == device->internals.dev )
      ata_device_execute_cmd(device);
  else
  {
      /* if not selected, only respond to EXECUTE DEVICE DIAGNOSTICS  */
      if (device->regs.command == EXECUTE_DEVICE_DIAGNOSTICS)
          ata_device_execute_cmd(device);
  }
}
 
 
/* Return contents of status register in pretty for */
static const char *ata_pretty_status(uint8_t status)
{
  static char str[50];
 
  str[0] = '\0';
  if(status & ATA_SR_BSY)
    strcat(str, "BUSY");
  if(status & ATA_SR_DRDY)
    strcat(str, "| READY");
  if(status & ATA_SR_DF)
    strcat(str, "| FAULT");
  if(status & ATA_SR_DSC)
    strcat(str, "| SEEKC");
  if(status & ATA_SR_DRQ)
    strcat(str, "| DRQ");
  if(status & ATA_SR_COR)
    strcat(str, "| COR");
  if(status & ATA_SR_IDX)
    strcat(str, "| IDX");
  if(status & ATA_SR_ERR)
    strcat(str, "| ERROR");
  return str;
}
 
/*
  D E V I C E S _ R E A D
*/
/* Read from devices                                                  */
short ata_devices_read(ata_devices *devices, char adr)
{
    ata_device *device;
 
    /* check for no connected devices                                 */
    if((!devices->device[0].conf.stream) && (!devices->device[1].conf.stream))
        ERR("ata_devices_read, no ata devices connected.\n");
    else
    {
      /* check if both device0 and device1 are connected              */
      if((devices->device[0].conf.stream) && (devices->device[1].conf.stream)) {
          /* get the current active device                            */
          if (devices->device[1].regs.device_head & ATA_DHR_DEV)
              device = &devices->device[1];
          else
              device = &devices->device[0];
      }
      else
      {
          /* only one device connected                                */
          if(devices->device[1].conf.stream)
              device = &devices->device[1];
          else
              device = &devices->device[0];
      }
 
      /* return data provided by selected device                      */
      switch (adr) {
        case ATA_ASR :
          TRACE("read alternate status register: %s\n",
                ata_pretty_status(device->regs.status));
	  if ( (device->regs.device_head & ATA_DHR_DEV) ==  device->internals.dev )
              return device -> regs.status;
	  else
	  {
	      TRACE("device0 responds for device1, asr = 0x00\n");
	      return 0; // return 0 when device0 responds for device1
	  }
 
        case ATA_CHR :
          TRACE("cylinder_high register read, value = 0x%02X\n",
	        device->regs.cylinder_high);
          return device -> regs.cylinder_high;
 
        case ATA_CLR :
          TRACE("cylinder_low register read, value = 0x%02X\n",
	         device->regs.cylinder_low);
          return device -> regs.cylinder_low;
 
        case ATA_DR  :
	  if(!device->regs.status & ATA_SR_DRQ) {
	     TRACE("data register read, while DRQ bit negated\n" );
	     return 0;
	  } else {
              uint16_t val = *device->internals.dbuf_ptr++;
              TRACE("data register read, value = 0x%04X, cnt = %3d\n", val,
	            device->internals.dbuf_cnt);
              if(!--device->internals.dbuf_cnt) {
                   device->regs.status &= ~ATA_SR_DRQ;
		   if(device->internals.end_t_func)
			device->internals.end_t_func(device);
	      }
	      return val;
	  }
 
        case ATA_DHR :
          TRACE("device_head register read, value = 0x%02X\n",
	         device->regs.device_head);
          return device -> regs.device_head;
 
        case ATA_ERR :
          TRACE("error register read, value = 0x%02X\n",
	         device->regs.error);
          return device -> regs.error;
 
        case ATA_SCR :
          TRACE("sectorcount register read, value = 0x%02X\n",
	         device->regs.sector_count);
          return device -> regs.sector_count;
 
        case ATA_SNR :
          TRACE("sectornumber register read, value = 0x%02X\n",
	         device->regs.sector_number);
          return device -> regs.sector_number;
 
        case ATA_SR:
          TRACE( "read status register: %s\n",
                 ata_pretty_status(device->regs.status) );
	  if ( (device->regs.device_head & ATA_DHR_DEV) ==  device->internals.dev)
              return device -> regs.status;
	  else
	  {
	      TRACE("device0 responds for device1, sr = 0x00\n");
	      return 0; // return 0 when device0 responds for device1
	  }
 
//        case ATA_DA   :
//          return device -> regs.status;
      }
    }
    return 0;
}
 
 
/*
  D E V I C E S _ W R I T E
*/
/* write to a single device                                           */
static void ata_device_write(ata_device *device, char adr, short value)
{
    switch (adr) {
        case ATA_CR  :
	    /*display debug information                               */
	    TRACE("command register written, value = 0x%02X\n", value);
 
            device->regs.command = value;
 
	    /* check command register settings and execute command    */
	    ata_device_do_command_register(device);
            break;
 
 
        case ATA_CHR :
	    /*display debug information                               */
	    TRACE("cylinder high register written, value = 0x%02X\n", value);
 
            device->regs.cylinder_high = value;
            break;
 
        case ATA_CLR :
	    /*display debug information                               */
	    TRACE("cylinder low register written, value = 0x%02X\n", value);
 
            device->regs.cylinder_low = value;
            break;
 
        case ATA_DR :
	    /*display debug information                               */
	    if(!device->regs.status & ATA_SR_DRQ) {
	       TRACE("data register write, while DRQ bit negated\n");
	       break;
	    } else {
                TRACE("data register write, value = 0x%04X, cnt = %3d\n",
	              value, device->internals.dbuf_cnt);
	        *device->internals.dbuf_ptr++ = value;
                if (!--device->internals.dbuf_cnt) {
                     device->regs.status &= ~ATA_SR_DRQ;
		     if(device->internals.end_t_func)
			  device->internals.end_t_func(device);
	        }
	    }
            device->regs.dataport_i = value;
            break;
 
        case ATA_DCR :
	    /*display debug information                               */
	    TRACE("device control register written, value = 0x%02X\n", value);
 
            device->regs.device_control = value;
            ata_device_do_control_register(device);
	    break;
 
        case ATA_DHR :
	    /*display debug information                               */
	    TRACE("device head register written, value = 0x%02X\n", value);
 
            device->regs.device_head = value;
            break;
 
        case ATA_FR  :
	    /*display debug information                               */
	    TRACE("features register written, value = 0x%02X\n", value);
 
            device->regs.features = value;
            break;
 
        case ATA_SCR :
	    /*display debug information                               */
	    TRACE("sectorcount register written, value = 0x%02X\n", value);
 
            device->regs.sector_count = value;
            break;
 
        case ATA_SNR :
	    /*display debug information                               */
	    TRACE("sectornumber register written, value = 0x%02X\n", value);
 
            device->regs.sector_number = value;
            break;
 
    } //endcase
}
 
/* Write to devices                                                   */
void ata_devices_write(ata_devices *devices, char adr, short value)
{
  /* check for no connected devices                                 */
  if(!devices->device[0].conf.stream && !devices->device[1].conf.stream)
    ERR("ata_devices_write, no ata devices connected.\n");
  else {
    /* first device                                                 */
    if(devices->device[0].conf.stream)
      ata_device_write(&devices->device[0], adr, value);
 
    /* second device                                                */
    if(devices->device[1].conf.stream)
      ata_device_write(&devices->device[1], adr, value);
  }
}
 
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.