static void _readMemory(int handle, ushort addr, ushort length)
    {
        int count;
        int i;

        byte[] data_in = new byte[length + 3];
        for (int j = 0; j < length + 3; j++)
        {
            data_in[j] = 0;
        }

        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_oe(handle, 1);

        // Queue the read command, address, and data
        CheetahApi.ch_spi_queue_ss(handle, 0x1);
        CheetahApi.ch_spi_queue_byte(handle, 1, 0x03);
        CheetahApi.ch_spi_queue_byte(handle, 1, (byte)((addr >> 8) & 0xff));
        CheetahApi.ch_spi_queue_byte(handle, 1, (byte)((addr >> 0) & 0xff));
        CheetahApi.ch_spi_queue_byte(handle, length, 0x00);
        CheetahApi.ch_spi_queue_ss(handle, 0);

        count = CheetahApi.ch_spi_batch_shift(handle, length + 3, data_in);

        if (count < 0)
        {
            Console.Write("error: {0:s}\n",
                          CheetahApi.ch_status_string(count));
        }
        else if (count != length + 3)
        {
            Console.Write("error: read {0:d} bytes (expected {1:d})\n",
                          count - 3, length);
        }

        // Dump the data to the screen
        Console.Write("\nData read from device:");
        for (i = 0; i < length; ++i)
        {
            if ((i & 0x0f) == 0)
            {
                Console.Write("\n{0:x4}:  ", addr + i);
            }

            Console.Write("{0:x2} ", data_in[i + 3] & 0xff);

            if (((i + 1) & 0x07) == 0)
            {
                Console.Write(" ");
            }
        }
        Console.Write("\n");
        Console.Out.Flush();
    }
    /*=====================================================================
     | FUNCTIONS
     | ====================================================================*/
    static void _blast(int handle, int length)
    {
        double elapsed;
        ulong  start = _timeMillis();

        // Queue the read sequence
        CheetahApi.ch_spi_queue_clear(handle);

        CheetahApi.ch_spi_queue_oe(handle, 1);
        CheetahApi.ch_spi_queue_ss(handle, 0);
        CheetahApi.ch_spi_queue_ss(handle, 0x1);

        int delay = 0;
        int j;

        for (j = 0; j < length; ++j)
        {
            CheetahApi.ch_spi_queue_byte(handle, 1, (byte)(j & 0xff));
            delay = CheetahApi.ch_spi_queue_delay_ns(handle, BYTE_DELAY);
        }
        Console.Write("Queued delay of {0:d} ns between bytes.\n", delay);
        Console.Out.Flush();

        CheetahApi.ch_spi_queue_ss(handle, 0);
        CheetahApi.ch_spi_queue_oe(handle, 0);

        elapsed = ((double)(_timeMillis() - start)) / 1000;
        Console.Write("Took {0:f2} seconds to queue the batch.\n", elapsed);
        Console.Out.Flush();

        // Perform the shift
        int batch = CheetahApi.ch_spi_batch_length(handle);

        byte[] data_in = new byte[batch];

        start = _timeMillis();
        int count = CheetahApi.ch_spi_batch_shift(handle, batch, data_in);

        elapsed = ((double)(_timeMillis() - start)) / 1000;
        Console.Write("Took {0:f2} seconds to shift the batch.\n", elapsed);
        Console.Out.Flush();

        if (count != batch)
        {
            Console.Write("Expected {0:d} bytes but only received {1:d} " +
                          "bytes\n", batch, count);
        }

        if (SHOW_DATA)
        {
            // Output the data to the screen
            Console.Write("\nData:");
            int i;
            for (i = 0; i < length; ++i)
            {
                if ((i & 0x07) == 0)
                {
                    Console.Write("\n{0:x4}:  ", i);
                }
                Console.Write("{0:x2}/{1:x2} ", (i & 0xff), data_in[i]);
            }
            Console.Write("\n");
            Console.Out.Flush();
        }
    }
Exemple #3
0
    static int _read(int handle, int addr, int length, out byte[] data_in)
    {
        // Convert address and length from KB to bytes
        addr   *= 1024;
        length *= 1024;

        // Create the buffer to receive the flash values into.
        data_in = new byte[length];
        for (int j = 0; j < length; j++)
        {
            data_in[j] = 0;
        }

        byte[] noresult = new byte[1];

        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_oe(handle, 1);

        // Set slave select to deasserted state, in case it was left
        // low by a previously interrupted transaction (ctrl-c).  This
        // will reset the state machine inside the flash.
        CheetahApi.ch_spi_queue_ss(handle, 0);
        CheetahApi.ch_spi_queue_ss(handle, 1);

        // Queue fast read command code.
        CheetahApi.ch_spi_queue_byte(handle, 1, 0x0b);

        // Queue 3 bytes of address.
        CheetahApi.ch_spi_queue_byte(handle, 1, (byte)(addr >> 16));
        CheetahApi.ch_spi_queue_byte(handle, 1, (byte)(addr >> 8));
        CheetahApi.ch_spi_queue_byte(handle, 1, (byte)(addr >> 0));

        // Queue dummy byte.
        CheetahApi.ch_spi_queue_byte(handle, 1, 0);

        // Shift the queued fast read command.
        int count = CheetahApi.ch_spi_batch_shift(handle, 0, data_in);

        if (count != 5)
        {
            Console.Error.Write("Expected 5 bytes from initial shift\n");
            return(1);
        }

        // Read the data.
        // Set the value to send while reading the flash.
        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_byte(handle, length, 0x00);
        count = CheetahApi.ch_spi_batch_shift(handle, length, data_in);
        if (count != length)
        {
            Console.Error.Write("Expected {0:d} bytes from read shift " +
                                "(got {1:d})\n", length, count);
            return(1);
        }

        // Dump the data to the screen
        //int i;
        //Console.Write("\nData read from device:");
        //for (i=0; i < length; ++i) {
        //    if ((i&0x0f) == 0)      Console.Write("\n{0:x4}:  ", addr+i);
        //    Console.Write("{0:x2} ", data_in[i] & 0xff);
        //    if (((i+1)&0x07) == 0)  Console.Write(" ");
        //}
        //Console.Write("\n");
        //Console.Out.Flush();

        // Clear the state of the bus.
        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_ss(handle, 0);
        CheetahApi.ch_spi_queue_oe(handle, 0);
        CheetahApi.ch_spi_batch_shift(handle, 0, noresult);

        return(0);
    }
Exemple #4
0
    static int _verify(int handle, int length, int blocksize)
    {
        if (length == 0 || blocksize == 0)
        {
            return(2);
        }
        byte[] noresult = new byte[1];

        int iter = (length - 1) / blocksize + 1;

        // Create the buffer to receive the flash values into.
        byte[] data_in = new byte[blocksize];
        for (int j = 0; j < blocksize; j++)
        {
            data_in[j] = 0;
        }

        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_oe(handle, 1);

        // Set slave select to deasserted state, in case it was left
        // low by a previously interrupted transaction (ctrl-c).  This
        // will reset the state machine inside the flash.
        CheetahApi.ch_spi_queue_ss(handle, 0);
        CheetahApi.ch_spi_queue_ss(handle, 1);

        // Queue fast read command code.
        CheetahApi.ch_spi_queue_byte(handle, 1, 0x0b);

        // Queue 3 bytes of address.
        CheetahApi.ch_spi_queue_byte(handle, 1, 0);
        CheetahApi.ch_spi_queue_byte(handle, 1, 0);
        CheetahApi.ch_spi_queue_byte(handle, 1, 0);

        // Queue dummy byte.
        CheetahApi.ch_spi_queue_byte(handle, 1, 0);

        // Shift the queued fast read command.
        int count = CheetahApi.ch_spi_batch_shift(handle, 5, data_in);

        if (count != 5)
        {
            Console.Write("Expected 5 bytes from initial shift\n");
            return(1);
        }

        // Set the value to send while reading the flash.
        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_byte(handle, blocksize, 0x00);

        int   i;
        ulong start = _timeMicroseconds();

        // Variables for the integrity check.
        uint integrity_counter = 0;
        int  integrity_errors  = 0;
        int  integrity_nb      = 0;

        _printProgress(0, 0, 0, blocksize);

        // Read one block at a time.
        for (i = 0; i < iter; ++i)
        {
            // Read the next block of data.
            count = CheetahApi.ch_spi_batch_shift(handle, blocksize,
                                                  data_in);
            if (count != blocksize)
            {
                Console.Write("Expected {0:d} bytes from block shift " +
                              "(got {1:d})\n",
                              blocksize, count);
                break;
            }

            // Check if the data in the flash matches a predefined
            // sequence.  Namely, there should be a running 32 bit
            // counter in the data.
            int j = 0;
            while (integrity_nb < INTEGRITY_CHECK_NUM_BYTES && j < count)
            {
                uint val = (uint)((data_in[j + 0] << 24) |
                                  (data_in[j + 1] << 16) |
                                  (data_in[j + 2] << 8) |
                                  (data_in[j + 3] << 0));

                if (val != integrity_counter % INTEGRITY_LOOP_SIZE)
                {
                    ++integrity_errors;
                }

                ++integrity_counter;
                integrity_nb += 4;
                j            += 4;
            }

            // Print out the progress.
            ulong currTime = _timeMicroseconds();

            _printProgress((i + 1) * 100 / iter,
                           ((double)(currTime - start)) / 1000000,
                           i, blocksize);
        }

        // Clear the state of the flash.
        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_ss(handle, 0);
        CheetahApi.ch_spi_queue_oe(handle, 0);
        CheetahApi.ch_spi_batch_shift(handle, 0, noresult);

        Console.Out.Flush();
        Console.Error.Write("\nThere were {0:d} data errors.\n",
                            integrity_errors);

        return((integrity_errors != 0) ? 2 : 0);
    }
Exemple #5
0
//suppose this is the block erase function,sector size is 4k ,block size is 32k/64k
//command d8 is block erase command
    static int _erase(int handle, int sector, int num)
    {
        byte[] noresult = new byte[1];

        // Reset the state of the bus.
        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_ss(handle, 0);
        CheetahApi.ch_spi_queue_oe(handle, 0);
        CheetahApi.ch_spi_batch_shift(handle, 0, noresult);

        int eraseAll = 0;

        if (sector == 0 && num == 2048)
        {
            eraseAll = 1;
        }

        String str;

        if (eraseAll != 0)
        {
            str = "Bulk";
        }
        else
        {
            str = "Block";
        }

        while (num != 0)
        {
            // Make sure the sector is a valid one.
            if (sector < 0 || sector > 2047)
            {
                break;
            }

            int addr = sector << 12;
            if (eraseAll == 0)
            {
                Console.Write("Erasing sector {0:d2} (bytes 0x{1:x6} " +
                              "to 0x{2:x6})...\n",
                              sector, addr, addr | 0xfff);
                Console.Out.Flush();
            }
            else
            {
                Console.Write("Erasing entire device...\n");
                Console.Out.Flush();
            }

            // Start the erase sequence.
            CheetahApi.ch_spi_queue_clear(handle);
            CheetahApi.ch_spi_queue_oe(handle, 1);

            // Queue the write enable instruction for the flash.
            CheetahApi.ch_spi_queue_ss(handle, 0x1);
            CheetahApi.ch_spi_queue_byte(handle, 1, 0x06);
            CheetahApi.ch_spi_queue_ss(handle, 0);

            CheetahApi.ch_spi_queue_ss(handle, 0x1);

            if (eraseAll == 0)
            {
                // Queue the sector erase command.
                CheetahApi.ch_spi_queue_byte(handle, 1, 0x20);
                CheetahApi.ch_spi_queue_byte(handle, 1,
                                             (byte)((addr >> 16) & 0xff));
                CheetahApi.ch_spi_queue_byte(handle, 1,
                                             (byte)((addr >> 8) & 0xff));
                CheetahApi.ch_spi_queue_byte(handle, 1,
                                             (byte)((addr >> 0) & 0xff));
            }
            else
            {
                // Queue the chip erase command.
                CheetahApi.ch_spi_queue_byte(handle, 1, 0xc7);
            }

            CheetahApi.ch_spi_queue_ss(handle, 0);
            // Shift the queued commands.  (Don't need the data back.)
            int batch = CheetahApi.ch_spi_batch_length(handle);
            int count = CheetahApi.ch_spi_batch_shift(handle, 0, noresult);
            if (count != batch)
            {
                Console.Write("Expected {0:d} bytes but only received " +
                              "{1:d} bytes\n", batch, count);
                return(1);
            }

            ulong start = _timeMicroseconds();
            CheetahApi.ch_spi_queue_clear(handle);
            CheetahApi.ch_spi_queue_ss(handle, 0x1);
            CheetahApi.ch_spi_queue_byte(handle, 1, 0x05);
            CheetahApi.ch_spi_queue_byte(handle, 1, 0x00);
            CheetahApi.ch_spi_queue_ss(handle, 0);
            while (true)
            {
                CheetahApi.ch_sleep_ms(10);

                byte[] status_in = new byte[2];
                CheetahApi.ch_spi_batch_shift(handle, 2, status_in);
                if ((status_in[1] & 0x01) == 0)
                {
                    break;
                }
            }
            ulong end = _timeMicroseconds();
            Console.Write("{0:s} erase took {1:f3} seconds\n",
                          str, (double)(end - start) / 1000000);
            Console.Out.Flush();

            if (eraseAll == 0)
            {
                ++sector;
                --num;
            }
            else
            {
                sector += 64;
                num     = 0;
            }
        }

        // Reset the state of the bus.
        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_oe(handle, 0);
        CheetahApi.ch_spi_batch_shift(handle, 0, noresult);


        return(0);
    }
Exemple #6
0
    static int _write(int handle, int addr, string binfile)
    {
        ulong start = _timeMicroseconds();
        //Read bin file
        BinaryReader br;

        try
        {
            br = new BinaryReader(new FileStream(binfile,
                                                 FileMode.Open));
        }
        catch (IOException e)
        {
            Console.WriteLine(e.Message + "\n Cannot open file.");
            return(-1);
        }

        int data_length     = Convert.ToInt32(br.BaseStream.Length);
        int data_pad_length = 1024 - data_length % 1024;

        byte[] bin_data = new byte[data_length];
        bin_data = br.ReadBytes(data_length);
        byte[] data      = new byte[data_length + data_pad_length];
        byte[] data_in   = new byte[data_length + data_pad_length];;
        int    addr_back = addr;

        bin_data.CopyTo(data, 0);

        if (data_pad_length != 0)
        {
            for (int i = 0; i < data_pad_length; i++)
            {
                data[data_length + i] = (byte)(255);
            }
        }


        br.Close();
        //Upate data_length to data_length + data_pad_length
        data_length = data_length + data_pad_length;

        // Buffer for outgoing data.
        byte[] data_page = new byte[4 + PAGE_SIZE];

        byte[] noresult  = new byte[1];
        byte[] data_back = new byte[5];

        //Erase first

        _erase(handle, addr / 4, (data.Length / (4 * 1024)) + 1);
        // Reset the state of the bus.
        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_ss(handle, 0);
        CheetahApi.ch_spi_queue_oe(handle, 1);
        CheetahApi.ch_spi_batch_shift(handle, 0, noresult);

        // Convert address and length from KB to bytes
        addr *= 1024;


        // Set the starting counter based on the address.
        int val   = addr / 4;
        int pages = 0;
        int retry = 0;

        while (data_length != 0)
        {
            // Start the write sequence.
            CheetahApi.ch_spi_queue_clear(handle);
            CheetahApi.ch_spi_queue_oe(handle, 1);


            // Send PAGE_WRITE_BATCH_SIZE number of pages to the Cheetah per
            // batch shift. Here we only program one page each batch
            for (int i = 0; i < PAGE_WRITE_BATCH_SIZE; i++)
            {
                // Check if we've reached the end.
                if (data_length == 0)
                {
                    break;
                }
                CheetahApi.ch_spi_queue_ss(handle, 0x1);
                CheetahApi.ch_spi_queue_byte(handle, 1, 0x06);
                CheetahApi.ch_spi_queue_ss(handle, 0);
                //while (((data_back[0]>>1 & 0x01) != 1) && (retry++ < 100) ) {
                //    // Queue the write enable instruction for the flash.
                //    //Write enable command 0x06
                //    CheetahApi.ch_spi_queue_ss(handle, 0x1);
                //    CheetahApi.ch_spi_queue_byte(handle, 1, 0x06);
                //    CheetahApi.ch_spi_queue_ss(handle, 0);
                //    CheetahApi.ch_spi_batch_shift(handle, 0, noresult);
                //    CheetahApi.ch_spi_queue_clear(handle);

                //    //Send RDSR Command check WEL bit = 1?
                //    //Shift out RDSR Command First 0x05
                //    CheetahApi.ch_spi_queue_ss(handle, 0x1);
                //    CheetahApi.ch_spi_queue_byte(handle, 1, 0x05);
                //    CheetahApi.ch_spi_queue_ss(handle, 0);
                //    CheetahApi.ch_spi_batch_shift(handle, 0,noresult);
                //    CheetahApi.ch_spi_queue_clear(handle);


                //    //Read One Byte Data Back
                //    CheetahApi.ch_spi_queue_ss(handle, 0x1);
                //    CheetahApi.ch_spi_queue_byte(handle, 1, 0x00);
                //    CheetahApi.ch_spi_queue_ss(handle, 0);
                //    CheetahApi.ch_spi_batch_shift(handle,1, data_back);
                //    CheetahApi.ch_spi_queue_clear(handle);

                //}
                //if (retry >= 100)
                //{
                //    Console.Write("Write Enable Failed.\n");
                //    Console.Out.Flush();
                //    return 1;
                //}
                // Queue the write instruction for the flash.
                // Page write command 0x02 + ADDR[3] + BYTE[256]
                CheetahApi.ch_spi_queue_ss(handle, 0x1);
                data_page[0] = 0x02;
                data_page[1] = (byte)((addr >> 16) & 0xff);
                data_page[2] = (byte)((addr >> 8) & 0xff);
                data_page[3] = (byte)((addr >> 0) & 0xff);

                Console.Write("addr = 0x{0:x6}; num bytes = {1:d}\n",
                              addr, PAGE_SIZE);
                Console.Out.Flush();

                // Set the data to be written to the flash to incrementing
                // 32-bit values starting from 0.
                int j = 0;
                while (j < PAGE_SIZE)
                {
                    data_page[4 + j] = data[j + pages * 256];
                    j += 1;
                }

                CheetahApi.ch_spi_queue_array(handle, 4 + PAGE_SIZE, data_page);
                CheetahApi.ch_spi_queue_ss(handle, 0);

                // Give the flash time to commit the written values.
                // Using ch_spi_queue_delay_ns is much more accurate than
                // using ch_sleep_ms.
                CheetahApi.ch_spi_queue_delay_ns(handle,
                                                 PAGE_PROGRAM_CYCLE_TIME_NS);
                addr        += PAGE_SIZE;
                data_length -= PAGE_SIZE;
                pages       += 1;
            }

            // Shift out the write command.  (Don't need the data back.)
            Console.Write("Shifting data\n");
            Console.Out.Flush();
            int batch = CheetahApi.ch_spi_batch_length(handle);
            int count = CheetahApi.ch_spi_batch_shift(handle, 0, noresult);
            if (count != batch)
            {
                Console.Write("Expected {0:d} bytes but only received " +
                              "{1:d} bytes\n", batch, count);
                return(1);
            }

            ////Wait Programming Page Finished. Check WIP = 0?
            //retry = 0;
            //data_back[0] = (byte)(0xff);
            //while (((data_back[0] & 0x01) != 0) && (retry++ < 100))
            //{

            //    CheetahApi.ch_spi_queue_clear(handle);
            //    //Send RDSR Command check WIP bit = 0?
            //    //Shift out RDSR Command First 0x05
            //    CheetahApi.ch_spi_queue_ss(handle, 0x1);
            //    CheetahApi.ch_spi_queue_byte(handle, 1, 0x05);
            //    CheetahApi.ch_spi_queue_ss(handle, 0);
            //    CheetahApi.ch_spi_batch_length(handle);
            //    CheetahApi.ch_spi_batch_shift(handle, 0, noresult);
            //    CheetahApi.ch_spi_queue_clear(handle);

            //    //Read One Byte Data Back
            //    CheetahApi.ch_spi_queue_ss(handle, 0x1);
            //    CheetahApi.ch_spi_queue_byte(handle, 1, 0x00);
            //    CheetahApi.ch_spi_queue_ss(handle, 0);
            //    CheetahApi.ch_spi_batch_shift(handle, 1, data_back);
            //    CheetahApi.ch_spi_queue_clear(handle);

            //}

            //if (retry >= 100)
            //{
            //    Console.Write("Write Enable Failed.\n");
            //    Console.Out.Flush();
            //    return 1;
            //}
            Console.Write("Shift complete\n");
            Console.Out.Flush();
            //    //Check Programming Result
            //    //Send RDSCUR Command 2B
            //    //Read Security Register bit 5
            //    CheetahApi.ch_spi_queue_ss(handle, 0x1);
            //    CheetahApi.ch_spi_queue_byte(handle, 1, 0x2b);
            //    CheetahApi.ch_spi_queue_ss(handle, 0);
            //    CheetahApi.ch_spi_batch_shift(handle, 0, noresult);
            //    CheetahApi.ch_spi_queue_clear(handle);

            //    //Read One Byte Data Back
            //    data_back[0] = (byte)(0xff);
            //    CheetahApi.ch_spi_queue_ss(handle, 0x1);
            //    CheetahApi.ch_spi_queue_byte(handle, 1, 0x00);
            //    CheetahApi.ch_spi_queue_ss(handle, 0);
            //    CheetahApi.ch_spi_batch_shift(handle, 1, data_back);
            //    CheetahApi.ch_spi_queue_clear(handle);

            //    //Check P_FAIL bit = 0? 0-> PP pass ,1-> PP fail
            //    if ((data_back[0]>>5 & 0x01) != 0)
            //    {
            //        Console.Write("PP Failed...,PFAIL Flag == 1.\n");
            //        Console.Out.Flush();
            //        return 1;
            //    }
        }

        // Reset the state of the bus.
        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_oe(handle, 0);
        CheetahApi.ch_spi_batch_shift(handle, 0, noresult);
        ulong end = _timeMicroseconds();

        Console.Write("Flashing take {0 :f3} seconds\n",
                      (double)(end - start) / 1000000);
        Console.Out.Flush();

        //verify the programming data is correct...
        _read(handle, addr_back, data.Length / 1024, out data_in);

        bool compare_result = false;

        for (int k = 0; k < data.Length; k++)
        {
            if (data[k] != data_in[k])
            {
                compare_result = true;
                break;
            }
        }
        end = _timeMicroseconds();
        Console.Write("Totally take {0 :f3} seconds\n", (double)(end - start) / 1000000);

        if (!compare_result)
        {
            Console.Write("Flashing Successfull...\n");
            Console.Out.Flush();
            return(0);
        }
        else
        {
            Console.Write("Flashing Failed...\n");
            Console.Out.Flush();
            return(-1);
        }
    }
    /*======================================================================
     | FUNCTIONS
     | =====================================================================*/
    static void _blast_async(int handle, int txnlen, int iter)
    {
        double elapsed = 0;

        byte[] noresult = new byte[1];

        // Make a simple queue to just assert OE.
        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_oe(handle, (byte)1);
        CheetahApi.ch_spi_batch_shift(handle, 0, noresult);


        // Queue the batch which is a sequence of SPI packets
        // (back-to-back) each of length 4.
        CheetahApi.ch_spi_queue_clear(handle);
        int i;
        int count = 0;

        byte[] data_out = new byte[4];
        for (i = 0; i < txnlen; ++i)
        {
            CheetahApi.ch_spi_queue_ss(handle, 0x1);

            data_out[0] = (byte)((count >> 24) & 0xff);
            data_out[1] = (byte)((count >> 16) & 0xff);
            data_out[2] = (byte)((count >> 8) & 0xff);
            data_out[3] = (byte)((count >> 0) & 0xff);

            ++count;

            CheetahApi.ch_spi_queue_array(handle, 4, data_out);
            CheetahApi.ch_spi_queue_ss(handle, 0x0);
        }

        ulong start = _timeMillis();

        // First, submit first batch
        CheetahApi.ch_spi_async_submit(handle);

        int n, ret;

        for (n = 0; n < iter - 1; ++n)
        {
            // Submit another batch, while the previous one is in
            // progress.  The application may even clear the current
            // batch queue and queue a different set of SPI
            // transactions before submitting this batch
            // asynchronously.
            CheetahApi.ch_spi_async_submit(handle);

            // The application can now perform some other functions
            // while the Cheetah is both finishing the previous batch
            // and shifting the current batch as well.  In order to
            // keep the Cheetah's pipe full, this entire loop must
            // complete AND another batch must be submitted
            // before the current batch completes.
            CheetahApi.ch_sleep_ms(25);

            // Collect the previous batch
            ret     = CheetahApi.ch_spi_async_collect(handle, 0, noresult);
            elapsed = ((double)(_timeMillis() - start)) / 1000;
            Console.Write("collected batch #{0:d3} in {1:f2} seconds\n",
                          n + 1, elapsed);
            if (ret < 0)
            {
                Console.Write("status error: {0:s}\n",
                              CheetahApi.ch_status_string(ret));
            }
            Console.Out.Flush();

            start = _timeMillis();

            // The current batch is now shifting out on the SPI
            // interface. The application can again do some more tasks
            // here but this entire loop must finish so that a new
            // batch is armed before the current batch completes.
            CheetahApi.ch_sleep_ms(25);
        }

        // Collect batch the last batch
        ret     = CheetahApi.ch_spi_async_collect(handle, 0, noresult);
        elapsed = ((double)(_timeMillis() - start)) / 1000;
        Console.Write("collected batch #{0:d3} in {1:f2} seconds\n",
                      n + 1, elapsed);
        if (ret < 0)
        {
            Console.Write("status error: {0:s}\n",
                          CheetahApi.ch_status_string(ret));
        }
        Console.Out.Flush();
    }
    /*=====================================================================
     | FUNCTIONS
     | ====================================================================*/
    static void _writeMemory(int handle, ushort addr, ushort length, int zero)
    {
        short i, j, n;
        int   batch, count;

        byte[] data_out = new byte[3 + PAGE_SIZE];

        byte[] noresult = new byte[1];

        // Write to the SPI EEPROM
        //
        // The AT25080A EEPROM has 32 byte pages.  Data can be written
        // in pages, to reduce the number of overall SPI transactions
        // executed through the Cheetah adapter.
        n = 0;
        while (n < length)
        {
            CheetahApi.ch_spi_queue_clear(handle);
            CheetahApi.ch_spi_queue_oe(handle, 1);

            // Send PAGE_WRITE_BATCH_SIZE number of pages to the Cheetah per
            // batch shift
            for (i = 0; i < PAGE_WRITE_BATCH_SIZE; i++)
            {
                if (n >= length)
                {
                    break;
                }

                // Send write enable command
                CheetahApi.ch_spi_queue_ss(handle, 0x1);
                CheetahApi.ch_spi_queue_byte(handle, 1, 0x06);
                CheetahApi.ch_spi_queue_ss(handle, 0);

                // Assemble the write command and address
                Console.Write("addr = 0x{0:x4}; ", addr);
                data_out[0] = (byte)0x02;
                data_out[1] = (byte)((addr >> 8) & 0xff);
                data_out[2] = (byte)((addr >> 0) & 0xff);

                // Assemble the data
                j = 3;
                do
                {
                    data_out[j++] = (zero != 0) ? (byte)0 : (byte)n;
                    ++addr; ++n;
                } while ((n < length) && ((addr & (PAGE_SIZE - 1)) != 0));

                Console.Write("num bytes = {0:d}\n", j - 3);
                Console.Out.Flush();

                // Queue the write transaction
                CheetahApi.ch_spi_queue_ss(handle, 0x1);
                CheetahApi.ch_spi_queue_array(handle, j, data_out);
                CheetahApi.ch_spi_queue_ss(handle, 0);

                // Put in a wait for the write cycle time
                CheetahApi.ch_spi_queue_delay_ns(handle, PAGE_WRITE_DELAY_NS);
            }

            // Shift the page writes
            // Don't need the results back from the shift
            Console.Write("Shifting data\n");
            Console.Out.Flush();
            batch = CheetahApi.ch_spi_batch_length(handle);
            count = CheetahApi.ch_spi_batch_shift(handle, 0, noresult);
            if (count != batch)
            {
                Console.Write("Expected {0:d} bytes but only received " +
                              "{1:d} bytes\n", batch, count);
                return;
            }
            Console.Write("Shift complete\n");
            Console.Out.Flush();
        }
    }
    /*=========================================================================
     | MAIN PROGRAM
     | ========================================================================*/
    public static void Main(String[] args)
    {
        int    handle  = 0;
        int    port    = 0;       // open port 0 by default
        int    bitrate = 0;
        int    mode    = 0;
        ushort addr    = 0;
        ushort length  = 0;
        String command = "";

        if (args.Length < 6)
        {
            print_usage();
            Environment.Exit(1);
        }

        port    = Convert.ToInt32(args[0]);
        bitrate = Convert.ToInt32(args[1]);
        mode    = Convert.ToInt32(args[2]);
        command = args[3];
        addr    = Convert.ToUInt16(args[4]);
        length  = Convert.ToUInt16(args[5]);

        // Open the device
        handle = CheetahApi.ch_open(port);
        if (handle <= 0)
        {
            Console.Error.Write(
                "Unable to open Cheetah device on port {0:d}\n", port);
            Console.Error.Write("Error code = {0:d} ({1:s})\n", handle,
                                CheetahApi.ch_status_string(handle));
            Environment.Exit(1);
        }
        Console.Write("Opened Cheetah device on port {0:d}\n", port);

        Console.Write("Host interface is {0:s}\n",
                      ((CheetahApi.ch_host_ifce_speed(handle)) != 0) ?
                      "high speed" : "full speed");

        // Ensure that the SPI subsystem is configured

        CheetahApi.ch_spi_configure(
            handle,
            (TotalPhase.CheetahSpiPolarity)(mode >> 1),
            (TotalPhase.CheetahSpiPhase)(mode & 1),
            CheetahSpiBitorder.CH_SPI_BITORDER_MSB, 0x0);

        Console.Write("SPI configuration set to mode {0:d}, {1:s} shift, " +
                      "SS[2:0] active low\n", mode, "MSB");
        Console.Out.Flush();

        // Power the target using the Cheetah adapter's power supply
        CheetahApi.ch_target_power(handle, CheetahApi.CH_TARGET_POWER_ON);
        CheetahApi.ch_sleep_ms(100);

        // Set the bitrate
        bitrate = CheetahApi.ch_spi_bitrate(handle, bitrate);
        Console.Write("Bitrate set to {0:d} kHz\n", bitrate);
        Console.Out.Flush();

        byte[] noresult = new byte[1];

        // Shift a dummy byte to clear the EEPROM state
        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_oe(handle, 1);
        CheetahApi.ch_spi_queue_ss(handle, 0x1);
        CheetahApi.ch_spi_queue_byte(handle, 1, 0x00);
        CheetahApi.ch_spi_queue_ss(handle, 0);
        CheetahApi.ch_spi_batch_shift(handle, 0, noresult);

        // Perform the requested operation
        if (command == "write")
        {
            _writeMemory(handle, addr, length, 0);
            Console.Write("Wrote to EEPROM\n");
        }
        else if (command == "read")
        {
            _readMemory(handle, addr, length);
        }
        else if (command == "zero")
        {
            _writeMemory(handle, addr, length, 1);
            Console.Write("Zeroed EEPROM\n");
        }
        else
        {
            Console.Write("unknown command: {0:s}\n", command);
        }

        // Close and exit
        CheetahApi.ch_close(handle);

        return;
    }
    /*=====================================================================
     | FUNCTIONS
     | ====================================================================*/
    static void _timing(int handle)
    {
        int cycles;
        int ns;

        byte[] noresult = new byte[1];

        // Test the SS timing
        CheetahApi.ch_spi_queue_clear(handle);
        Console.Write("Testing inter-SS delays...\n");
        Console.Out.Flush();
        CheetahApi.ch_spi_queue_oe(handle, 1);
        CheetahApi.ch_spi_queue_ss(handle, 0x1);

        cycles = CheetahApi.ch_spi_queue_delay_cycles(handle, 51);
        Console.Write("  Queued delay of {0:d} cycles within first SS " +
                      "assert/deassert.\n", cycles);
        Console.Out.Flush();
        CheetahApi.ch_spi_queue_ss(handle, 0);

        CheetahApi.ch_spi_queue_ss(handle, 0x1);

        ns = CheetahApi.ch_spi_queue_delay_ns(handle, 1500000);
        Console.Write("  Queued delay of {0:d} ns within second SS " +
                      "assert/deassert.\n", ns);
        Console.Out.Flush();
        CheetahApi.ch_spi_queue_ss(handle, 0);

        CheetahApi.ch_spi_batch_shift(handle, 0, noresult);

        // Test data timing in read mode
        Console.Write("Testing inter-data (read) delays...\n");
        Console.Out.Flush();
        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_ss(handle, 0x1);

        CheetahApi.ch_spi_queue_byte(handle, 1, 0xca);
        ns = CheetahApi.ch_spi_queue_delay_ns(handle, 250000);
        Console.Write("  Queued delay of {0:d} ns after first byte " +
                      "(0xca).\n", ns);
        Console.Out.Flush();

        CheetahApi.ch_spi_queue_byte(handle, 2, 0xfe);
        cycles = CheetahApi.ch_spi_queue_delay_cycles(handle, 995);
        Console.Write("  Queued delay of {0:d} cycles after second byte " +
                      "(0xfe).\n", cycles);
        Console.Out.Flush();

        CheetahApi.ch_spi_queue_byte(handle, 3, 0x00);
        cycles = CheetahApi.ch_spi_queue_delay_cycles(handle, 20);
        Console.Write("  Queued delay of {0:d} cycles after last byte " +
                      "(0x00).\n", cycles);
        Console.Out.Flush();

        CheetahApi.ch_spi_queue_ss(handle, 0);

        byte[] data_in = new byte[6];
        CheetahApi.ch_spi_batch_shift(handle, 6, data_in);


        // Test data timing with write mode
        Console.Write("Testing inter-data (write) delays...\n");
        Console.Out.Flush();
        CheetahApi.ch_spi_queue_clear(handle);
        CheetahApi.ch_spi_queue_ss(handle, 0x1);

        CheetahApi.ch_spi_queue_byte(handle, 1, 0xba);
        ns = CheetahApi.ch_spi_queue_delay_ns(handle, 80000);
        Console.Write("  Queued delay of {0:d} ns after first byte " +
                      "(0xba).\n", ns);
        Console.Out.Flush();

        CheetahApi.ch_spi_queue_byte(handle, 2, 0xbe);
        cycles = CheetahApi.ch_spi_queue_delay_cycles(handle, 995);
        Console.Write("  Queued delay of {0:d} cycles after second byte " +
                      "(0xbe).\n", cycles);
        Console.Out.Flush();

        CheetahApi.ch_spi_queue_byte(handle, 3, 0x00);
        cycles = CheetahApi.ch_spi_queue_delay_cycles(handle, 20);
        Console.Write("  Queued delay of {0:d} cycles after last byte " +
                      "(0x00).\n", cycles);
        Console.Out.Flush();

        CheetahApi.ch_spi_queue_ss(handle, 0);
        CheetahApi.ch_spi_queue_oe(handle, 0);

        CheetahApi.ch_spi_batch_shift(handle, 1, data_in);
    }