pharos
Posts: 5
Joined: Thu Jul 12, 2012 10:05 pm

Bit-banging a TSOP-48 NAND Flash with the RPi

Sat Sep 08, 2012 12:15 pm

Hi,

I'm trying to bit-bang a 3.3V NAND Flash which has a very fine pitch (0.5mm) using a solderless clip called the 360 Clip. Although it was originally meant to hack game consoles (hence the name), I'm attempting to reuse it as a generic TSOP NAND reader/writer. The most interesting thing about this piece, in addition to being solderless, is the very low price as compared to commercial NAND programmers (which usually require desoldering the chip as well).

The following link has a full-resolution picture of my setup:

https://picasaweb.google.com/1078242449 ... 7118336754

So I don't have the original PSU for the board I'm using as a guinea pig, so I'm using the 3.3V from the RPi. I noticed when plugging the power other parts of the board come alive, leading to the RPi (sometimes) resetting do to overcurrent I suppose. I'm thinking this is causing the issue I'm having (not being able to talk to the flash at all, even READ ID doesn't produce a result, I keep reading all 0s). I've checked the timings, the wirings and my own C code multiple times. What do you think might be the problem?

For the reference, here's my test code (and yes, I did check it does correctly output 0s and 1s, and reading from the data lines also work).

Code: Select all

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdio.h>
#include <fcntl.h>

//#define DEBUG 1

#define BCM2708_PERI_BASE	0x20000000
#define GPIO_BASE	 	(BCM2708_PERI_BASE + 0x200000)

#define N_WRITE_PROTECT		0
#define N_WRITE_ENABLE 		21
#define ADDRESS_LATCH_ENABLE	4
#define COMMAND_LATCH_ENABLE	17
#define N_READ_ENABLE		18
#define N_CHIP_ENABLE		22

int data_to_gpio_map[8] = { 23, 24, 25, 8, 7, 10, 9, 11 };

#define DIRECTION_DATA8_IN  1
#define DIRECTION_DATA8_OUT 2

volatile unsigned int *gpio;

inline void INP_GPIO(int g)
{
#ifdef DEBUG
	printf("setting direction of GPIO#%d to input\n", g);
#endif
	(*(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)));
}

inline void OUT_GPIO(int g)
{
	INP_GPIO(g);
#ifdef DEBUG
	printf("setting direction of GPIO#%d to output\n", g);
#endif
	*(gpio+((g)/10)) |= (1<<(((g)%10)*3));
}

int current_data_direction = 0;

inline void GPIO_SET_1(int g)
{
#ifdef DEBUG
	printf("setting GPIO#%d to 1\n", g);
#endif
	*(gpio +  7)  = 1 << g;
}

inline void GPIO_SET_0(int g)
{
#ifdef DEBUG
	printf("setting GPIO#%d to 0\n", g);
#endif
	*(gpio + 10)  = 1 << g;
}

inline int GPIO_READ(int g)
{
	int x = (*(gpio + 13) & (1 << g)) >> g;
#ifdef DEBUG
	printf("GPIO#%d reads as %d\n", g, x);
#endif
	return x;
}

inline void set_data_direction_in(void)
{
	int i;
	if (current_data_direction != DIRECTION_DATA8_IN) {
#ifdef DEBUG
		printf("data direction => IN\n");
#endif
		for (i = 0; i < 8; i++)
			INP_GPIO(data_to_gpio_map[i]);
		current_data_direction = DIRECTION_DATA8_IN;
	}
}

inline void set_data_direction_out(void)
{
	int i;
	if (current_data_direction != DIRECTION_DATA8_OUT) {
#ifdef DEBUG
		printf("data direction => OUT\n");
#endif
		for (i = 0; i < 8; i++)
			OUT_GPIO(data_to_gpio_map[i]);
		current_data_direction = DIRECTION_DATA8_OUT;
	}
}

inline int GPIO_DATA8_IN(void)
{
	int i, data;
	for (i = data = 0; i < 8; i++, data = data << 1) {
		data |= GPIO_READ(data_to_gpio_map[7 - i]);
	}
	data >>= 1;
#ifdef DEBUG
	printf("GPIO_DATA8_OUT: data=%02x\n", data);
#endif
	return data;
}

inline void GPIO_DATA8_OUT(int data)
{
	int i;
#ifdef DEBUG
	printf("GPIO_DATA8_OUT: data=%02x\n", data);
#endif
	for (i = 0; i < 8; i++, data >>= 1) {
		if (data & 1)
			GPIO_SET_1(data_to_gpio_map[i]);
		else
			GPIO_SET_0(data_to_gpio_map[i]);
	}
}

int delay;
int shortpause()
{
	int i;
	volatile static int dontcare = 0;
	for (i = 0; i < delay; i++) {
		dontcare++;
	}
}

int main(int argc, char **argv)
{ 
	int mem_fd, i, j;
	int buf[4096];

	delay = atoi(argv[1]);

	if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0) {
		perror("open /dev/mem");
		return -1;
	}

	if ((gpio = (volatile unsigned int *) mmap((caddr_t) 0x13370000, 4096, PROT_READ|PROT_WRITE,
						MAP_SHARED|MAP_FIXED, mem_fd, GPIO_BASE)) == MAP_FAILED) {
		perror("mmap GPIO_BASE");
		return -1;
	}

	OUT_GPIO(N_WRITE_PROTECT);
	GPIO_SET_1(N_WRITE_PROTECT);

	OUT_GPIO(N_READ_ENABLE);
	GPIO_SET_1(N_READ_ENABLE);

	OUT_GPIO(N_WRITE_ENABLE);
	GPIO_SET_1(N_WRITE_ENABLE);

	OUT_GPIO(COMMAND_LATCH_ENABLE);
	GPIO_SET_0(COMMAND_LATCH_ENABLE);

	OUT_GPIO(ADDRESS_LATCH_ENABLE);
	GPIO_SET_0(ADDRESS_LATCH_ENABLE);

	OUT_GPIO(N_CHIP_ENABLE);
	GPIO_SET_0(N_CHIP_ENABLE);

	sleep(1);

	GPIO_SET_1(COMMAND_LATCH_ENABLE); shortpause();
	GPIO_SET_0(N_WRITE_ENABLE);
	set_data_direction_out(); GPIO_DATA8_OUT(0x90); // Read ID byte 1
	shortpause();
	GPIO_SET_1(N_WRITE_ENABLE);
	shortpause(); set_data_direction_in();
	GPIO_SET_0(COMMAND_LATCH_ENABLE);
	GPIO_SET_1(COMMAND_LATCH_ENABLE);
	shortpause();
	GPIO_SET_0(N_WRITE_ENABLE);
	set_data_direction_out(); GPIO_DATA8_OUT(0x00); // Read ID byte 2
	shortpause();
	GPIO_SET_1(N_WRITE_ENABLE);
	shortpause(); set_data_direction_in();
	GPIO_SET_0(ADDRESS_LATCH_ENABLE);
	shortpause();
	shortpause();
	shortpause();

	for (i = 0; i < 5; i++) {
		GPIO_SET_0(N_READ_ENABLE);
		shortpause();
		GPIO_SET_1(N_READ_ENABLE);
		buf[i] = GPIO_DATA8_IN();
		shortpause();
	}
	for (i = 0; i < 5; i++)
		printf("%02x", buf[i]);
	printf("\n");

	GPIO_SET_1(N_CHIP_ENABLE);

	return 0;
}
Attachments
IMG_0010a.JPG
IMG_0010a.JPG (56.75 KiB) Viewed 23050 times

pharos
Posts: 5
Joined: Thu Jul 12, 2012 10:05 pm

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Sat Sep 08, 2012 7:02 pm

It works! Just a small mistake, I inverted command latch and address latch in address cycle 1.

Code: Select all

[email protected]:~# ./a.out 1
add8109544
Yes, that's a 5-byte NAND Flash ID above :)

pharos
Posts: 5
Joined: Thu Jul 12, 2012 10:05 pm

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Sun Sep 09, 2012 3:46 pm

So the NAND ID above is incorrect, it should be DA not D8. I've noticed the 360 Clip is extremely sensitive to the current position, which can be tiresome since the correct one must be maintained by applying pressure at the right spots when reading/writing the full NAND.

In any case, here's the most recent code:
rpi-tsop48-nand-v1.zip
(3.42 KiB) Downloaded 1641 times
. I haven't added black erase/programming yet, but it should be straightforward. The code deals with the false contact problem issue by simply retrying the current page read in case of a problem, which allows moving the clip to eliminate the false contact.

By the way, why can't we post .c and .txt files in the forum, but zip is allowed? This seems somewhat absurd.

Sizco
Posts: 4
Joined: Sun Apr 07, 2013 10:42 am

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Sun Apr 07, 2013 10:44 am

Hi,
the link to your setup is broken.
Can you upload the picture again oder anybody else, please?
I need information how to connect the clip pins to the gpio pins.

Thanks.

Sizco
Posts: 4
Joined: Sun Apr 07, 2013 10:42 am

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Wed Apr 10, 2013 8:06 pm

Does anybody have an idea how to connect the nand clip with the rpi?
When I try to connect 3,3v and GND to the clip und press the clip on the nand chip, the pri makes a reset. I think i have to take the power from the psu.
But i don't know whick pins of the clip have to connect to which pins on the rpi.

fdufnews
Posts: 289
Joined: Fri Oct 07, 2011 5:37 pm

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Thu Apr 11, 2013 9:29 am

External load on 3,3V is limited to 30mA

Sizco
Posts: 4
Joined: Sun Apr 07, 2013 10:42 am

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Thu Apr 11, 2013 10:06 am

Ok, then it's better to use the external psu.
My problem is that I don't know how to connect the clip.

This is the clip pinout:
Image

The GND, VCC and IO pins are not the problem.

Xumpy
Posts: 15
Joined: Tue Jun 04, 2013 9:23 pm

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Tue Jun 04, 2013 9:26 pm

Ok, So far I have the following for the links to the 360clip:

FRB1 : GPIO 1
RE : GPIO 18
FCE1 : GPIO 22
CLE : GPIO 17
ALE : GPIO 4
WE : GPIO 21
WP : GPIO 0
I/00 : GPIO 23
I/01 : GPIO 24
I/02 : GPIO 25
I/03 : GPIO 8
I/04 : GPIO 7
I/05 : GPIO 10
I/06 : GPIO 9
I/07 : GPIO 11

But I keep getting all zero's. When I apply pressure I sometimes get all 03 or 02 or I even saw 01 at a time.

Is it possible that this is because the power for my nand comes from the rpi and not an external psu?

I'm testing it on an xbox360. But I would like to use it for broken USB sticks.

Thnx,

Regards

Xump

Sizco
Posts: 4
Joined: Sun Apr 07, 2013 10:42 am

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Thu Jun 13, 2013 7:34 pm

Hi Xumpy,

is the script recognizing your NAND chip with your setup?
You can test this with the parameter "read_id".

Xumpy
Posts: 15
Joined: Tue Jun 04, 2013 9:23 pm

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Mon Jun 17, 2013 1:20 pm

Hey Sizco

Nope, my nand is not detected. Nor my xbox nand nor my usb nand.
Maybe I've connected something wrong but I don't think so. Could also be that my 360clip is broken.

I should learn how to etch smd.

Could someone confirm me that my wiring that I posted above is correct?

Thnx

Regards

Xump

User avatar
PHPower
Posts: 84
Joined: Tue Jan 01, 2013 7:48 pm
Location: PACA
Contact: Website

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Tue Aug 13, 2013 11:22 am

Hi !

I'm about to buy the 360 Clip to dump my PS3 NAND.
I saw that Xumpy told how to connect the Pi to Clip
FRB1 : GPIO 1
RE : GPIO 18
FCE1 : GPIO 22
CLE : GPIO 17
ALE : GPIO 4
WE : GPIO 21
WP : GPIO 0
I/00 : GPIO 23
I/01 : GPIO 24
I/02 : GPIO 25
I/03 : GPIO 8
I/04 : GPIO 7
I/05 : GPIO 10
I/06 : GPIO 9
I/07 : GPIO 11
But I didn't really understood how to give power to the NAND.

Thanks,

jbri
Posts: 1
Joined: Wed Jan 22, 2014 12:51 am

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Wed Jan 22, 2014 1:21 am

I've connected my pi to the clip and I give power to the nand externally via the psu of the device but every time I try read_id, it returns identical hex values. I've also tried reading a chip from an old usb device, but I get the same issue. Maybe it's just my poor soldering :oops: (my 360 clip 'side-board' didn't come with the pin connectors soldered).

Help anyone? I followed the wiring guide Xumpy posted.
Attachments
rp_x360_solder_8.png
side-board
rp_x360_solder_8.png (57.32 KiB) Viewed 19621 times

is0-mick
Posts: 1
Joined: Mon Oct 13, 2014 8:37 am

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Mon Oct 13, 2014 8:45 am

I was trying to read a TC58NVG1S3 chip with the above code, but was having lots of trouble with read errors, and the program failing with too many retries.

After a bit of investigation I think there is an error in the code.

Code: Select all

		for (i = 0; i < PAGE_SIZE; i++) {
			GPIO_SET_0(N_READ_ENABLE);
			shortpause();
			GPIO_SET_1(N_READ_ENABLE);
			buf[i + n] = GPIO_DATA8_IN();
			shortpause();
		}
is actually reading the data after it has set read enable high, and according to the datasheet, it should be reading while N_READ_ENABLE is low.

Code: Select all

		for (i = 0; i < PAGE_SIZE; i++) {
			GPIO_SET_0(N_READ_ENABLE);
			shortpause();
			buf[i + n] = GPIO_DATA8_IN();  <--- read should be here (while N_READ GPIO is set to 0 )
			GPIO_SET_1(N_READ_ENABLE);
			shortpause();
		}
After moving the buf[i + n] = GPIO_DATA8_IN(); at lines 270 and 395 above the GPIO_SET_1(N_READ_ENABLE);
I was able to read the flash :)

Mick

elementalist666
Posts: 1
Joined: Sat Feb 06, 2016 5:45 pm

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Sat Feb 06, 2016 5:48 pm

Anyway that you could possibly add the programming and erase part of the script? And or give me a hint on how to do it im going to try to downgrade a ps3 with you script I just really don't understand how to change the script to program and erase thanks!

murphysLaw
Posts: 1
Joined: Sun Mar 27, 2016 1:23 am

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Sun Mar 27, 2016 1:29 am

could you please tell me what these 360 clips are called i am very interested;and, searching "360 clip" on ebay turns up a bunch of random crap. Thanks in advance....

dwjp_78
Posts: 1
Joined: Sun Oct 21, 2012 5:55 pm

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Sat Apr 30, 2016 4:45 am

They are solderless nand clips this Image is what they look like. They have them at http://www.360-clip.com/ but you could probably find them on ebay by searching "360-Clip"

testpost
Posts: 2
Joined: Wed Apr 27, 2016 11:47 pm

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Sun May 01, 2016 7:18 pm

No one ever responded to Xumpy's question about the appropriate pin connections. Is Xumpy's assertion correct, even though he could not get it to work?

Can anyone else on here confirm that they have used the above-mentioned pin-outs with the latest code with success?

testpost
Posts: 2
Joined: Wed Apr 27, 2016 11:47 pm

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Thu May 05, 2016 2:01 pm

Bump:

On top of the above question, how does this code work with a RPI 3 Model B? GPIO0/GPIO1 no longer exist (can I default to 2 and 3?). Besides that, will it work with the latest RPI?

Thanks!

Exodus_5467
Posts: 1
Joined: Mon Aug 22, 2016 1:07 am

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Mon Aug 22, 2016 1:11 am

Anyone knows the proper wiring to do this on a Pi2?
The GPIO naming is a bit vague.

User avatar
DougieLawson
Posts: 36320
Joined: Sun Jun 16, 2013 11:19 pm
Location: Basingstoke, UK
Contact: Website Twitter

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Mon Aug 22, 2016 4:24 pm

GPIO naming is not vague at all, there are just three ways to do it (board, Broadcom (BCM) or WiringPi).

http://pinout.xyz
Note: Having anything humorous in your signature is completely banned on this forum. Wear a tin-foil hat and you'll get a ban.

Any DMs sent on Twitter will be answered next month.

This is a doctor free zone.

Stoobs
Posts: 1
Joined: Sat Dec 22, 2018 10:10 pm

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Sat Dec 22, 2018 10:17 pm

Did anyone manage to get this working?

I've tried compiling the listed code with the tweaks suggested above (and updating the memory range for the pi3), but all I get are the following errors?

Code: Select all

/tmp/cceT84Mu.o: In function `main':
rpi-DumpFlash.c:(.text+0x104): undefined reference to `INP_GPIO'
rpi-DumpFlash.c:(.text+0x10c): undefined reference to `OUT_GPIO'
rpi-DumpFlash.c:(.text+0x114): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x11c): undefined reference to `OUT_GPIO'
rpi-DumpFlash.c:(.text+0x124): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x12c): undefined reference to `OUT_GPIO'
rpi-DumpFlash.c:(.text+0x134): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x13c): undefined reference to `OUT_GPIO'
rpi-DumpFlash.c:(.text+0x144): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x14c): undefined reference to `OUT_GPIO'
rpi-DumpFlash.c:(.text+0x154): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x15c): undefined reference to `OUT_GPIO'
rpi-DumpFlash.c:(.text+0x164): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x188): undefined reference to `GPIO_SET_1'
/tmp/cceT84Mu.o: In function `read_id':
rpi-DumpFlash.c:(.text+0x42c): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x438): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x43c): undefined reference to `set_data_direction_out'
rpi-DumpFlash.c:(.text+0x444): undefined reference to `GPIO_DATA8_OUT'
rpi-DumpFlash.c:(.text+0x450): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x458): undefined reference to `set_data_direction_in'
rpi-DumpFlash.c:(.text+0x460): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x46c): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x474): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x478): undefined reference to `set_data_direction_out'
rpi-DumpFlash.c:(.text+0x480): undefined reference to `GPIO_DATA8_OUT'
rpi-DumpFlash.c:(.text+0x48c): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x494): undefined reference to `set_data_direction_in'
rpi-DumpFlash.c:(.text+0x49c): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x4b4): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x4bc): undefined reference to `GPIO_DATA8_IN'
rpi-DumpFlash.c:(.text+0x4e0): undefined reference to `GPIO_SET_1'
/tmp/cceT84Mu.o: In function `send_command_address':
rpi-DumpFlash.c:(.text+0x600): undefined reference to `set_data_direction_out'
rpi-DumpFlash.c:(.text+0x608): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x614): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x61c): undefined reference to `GPIO_DATA8_OUT'
rpi-DumpFlash.c:(.text+0x628): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x634): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x640): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x654): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x660): undefined reference to `page_to_address'
rpi-DumpFlash.c:(.text+0x66c): undefined reference to `GPIO_DATA8_OUT'
rpi-DumpFlash.c:(.text+0x678): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x69c): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x6a8): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x6b4): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x6bc): undefined reference to `GPIO_DATA8_OUT'
rpi-DumpFlash.c:(.text+0x6c8): undefined reference to `GPIO_SET_1'
rpi-DumpFlash.c:(.text+0x6d4): undefined reference to `GPIO_SET_0'
/tmp/cceT84Mu.o: In function `read_pages':
rpi-DumpFlash.c:(.text+0x790): undefined reference to `GPIO_READ'
rpi-DumpFlash.c:(.text+0x8bc): undefined reference to `GPIO_READ'
rpi-DumpFlash.c:(.text+0x90c): undefined reference to `set_data_direction_in'
rpi-DumpFlash.c:(.text+0x920): undefined reference to `GPIO_READ'
rpi-DumpFlash.c:(.text+0x99c): undefined reference to `GPIO_SET_0'
rpi-DumpFlash.c:(.text+0x9b0): undefined reference to `GPIO_DATA8_IN'
rpi-DumpFlash.c:(.text+0x9d0): undefined reference to `GPIO_SET_1'
collect2: error: ld returned 1 exit status
This is the updated listing:

Code: Select all

/*
    Raspberry Pi / 360-Clip based 8-bit NAND reader

    Copyright (C) 2012  pharos

    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 3 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, see <http://www.gnu.org/licenses/>.
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>

//#define DEBUG 1

#define PAGE_SIZE 2112
#define MAX_WAIT_READ_BUSY	1000000

#define BCM2708_PERI_BASE	0x3F000000
#define GPIO_BASE	 	(BCM2708_PERI_BASE + 0x200000)

#define N_WRITE_PROTECT		0 // pulled up by RPi, this is useful
#define N_READ_BUSY		1 // pulled up by RPi, this is also useful

// rest of GPIOs have been chose arbitrarily, with the only constraint of not using
// GPIO 14 (TXD)/GPIO 15 (RXD)/06 (GND) on P1. instead I use GND on P2 header, pin 8

// IMPORTANT: BE VERY CAREFUL TO CONNECT VCC TO P1-01 (3.3V) AND *NOT* P1-02 (5V) !!

#define N_WRITE_ENABLE 		21
#define ADDRESS_LATCH_ENABLE	4
#define COMMAND_LATCH_ENABLE	17
#define N_READ_ENABLE		18
#define N_CHIP_ENABLE		22

int data_to_gpio_map[8] = { 23, 24, 25, 8, 7, 10, 9, 11 }; // 23 is NAND IO 0, etc.

volatile unsigned int *gpio;

inline void INP_GPIO(int g)
{
#ifdef DEBUG
	printf("setting direction of GPIO#%d to input\n", g);
#endif
	(*(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)));
}

inline void OUT_GPIO(int g)
{
	INP_GPIO(g);
#ifdef DEBUG
	printf("setting direction of GPIO#%d to output\n", g);
#endif
	*(gpio+((g)/10)) |= (1<<(((g)%10)*3));
}

inline void GPIO_SET_1(int g)
{
#ifdef DEBUG
	printf("setting GPIO#%d to 1\n", g);
#endif
	*(gpio +  7)  = 1 << g;
}

inline void GPIO_SET_0(int g)
{
#ifdef DEBUG
	printf("setting GPIO#%d to 0\n", g);
#endif
	*(gpio + 10)  = 1 << g;
}

inline int GPIO_READ(int g)
{
	int x = (*(gpio + 13) & (1 << g)) >> g;
#ifdef DEBUG
	printf("GPIO#%d reads as %d\n", g, x);
#endif
	return x;
}

inline void set_data_direction_in(void)
{
	int i;
#ifdef DEBUG
	printf("data direction => IN\n");
#endif
	for (i = 0; i < 8; i++)
		INP_GPIO(data_to_gpio_map[i]);
}

inline void set_data_direction_out(void)
{
	int i;
#ifdef DEBUG
	printf("data direction => OUT\n");
#endif
	for (i = 0; i < 8; i++)
		OUT_GPIO(data_to_gpio_map[i]);
}

inline int GPIO_DATA8_IN(void)
{
	int i, data;
	for (i = data = 0; i < 8; i++, data = data << 1) {
		data |= GPIO_READ(data_to_gpio_map[7 - i]);
	}
	data >>= 1;
#ifdef DEBUG
	printf("GPIO_DATA8_IN: data=%02x\n", data);
#endif
	return data;
}

inline void GPIO_DATA8_OUT(int data)
{
	int i;
#ifdef DEBUG
	printf("GPIO_DATA8_OUT: data=%02x\n", data);
#endif
	for (i = 0; i < 8; i++, data >>= 1) {
		if (data & 1)
			GPIO_SET_1(data_to_gpio_map[i]);
		else
			GPIO_SET_0(data_to_gpio_map[i]);
	}
}

int delay = 1;
int shortpause()
{
	int i;
	volatile static int dontcare = 0;
	for (i = 0; i < delay; i++) {
		dontcare++;
	}
}

int main(int argc, char **argv)
{ 
	int mem_fd;

	if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0) {
		perror("open /dev/mem, are you root?");
		return -1;
	}

	if ((gpio = (volatile unsigned int *) mmap((caddr_t) 0x13370000, 4096, PROT_READ|PROT_WRITE,
						MAP_SHARED|MAP_FIXED, mem_fd, GPIO_BASE)) == MAP_FAILED) {
		perror("mmap GPIO_BASE");
		close(mem_fd);
		return -1;
	}

	INP_GPIO(N_READ_BUSY);

	OUT_GPIO(N_WRITE_PROTECT);
	GPIO_SET_1(N_WRITE_PROTECT);

	OUT_GPIO(N_READ_ENABLE);
	GPIO_SET_1(N_READ_ENABLE);

	OUT_GPIO(N_WRITE_ENABLE);
	GPIO_SET_1(N_WRITE_ENABLE);

	OUT_GPIO(COMMAND_LATCH_ENABLE);
	GPIO_SET_0(COMMAND_LATCH_ENABLE);

	OUT_GPIO(ADDRESS_LATCH_ENABLE);
	GPIO_SET_0(ADDRESS_LATCH_ENABLE);

	OUT_GPIO(N_CHIP_ENABLE);
	GPIO_SET_0(N_CHIP_ENABLE);

	if (argc < 3) {
usage:
		GPIO_SET_1(N_CHIP_ENABLE);
		printf("usage: %s <delay> <command> ...\n" \
			"\t<delay> is used to slow down operations (50 should work, increase in case of bad reads)\n" \
			"\tthis program assumes PAGE_SIZE == %d (this can be changed at the top of the source)\n" \
			"available commands:\n" \
			"\tread_id (no arguments) : read the 5-byte device ID\n" \
			"\tread_full <page number> <# of pages> <output filename> : read N pages including spare\n" \
			"\tread_data <page number> <# of pages> <output filename> : read N pages, discard spare\n",
			argv[0], PAGE_SIZE);
		close(mem_fd);
		return -1;
	}

	delay = atoi(argv[1]);
	if (delay < 20) {
		printf("delay must be >= 20\n");
		return -1;
	}

	if (strcmp(argv[2], "read_id") == 0) {
		return read_id(NULL);
	}

	if (strcmp(argv[2], "read_full") == 0) {
		if (argc != 6) goto usage;
		if (atoi(argv[4]) <= 0) {
			printf("# of pages must be > 0\n");
			return -1;
		}
		return read_pages(atoi(argv[3]), atoi(argv[4]), argv[5], 1);
	}

	if (strcmp(argv[2], "read_data") == 0) {
		if (argc != 6) goto usage;
		if (atoi(argv[4]) <= 0) {
			printf("# of pages must be > 0\n");
			return -1;
		}
		return read_pages(atoi(argv[3]), atoi(argv[4]), argv[5], 0);
	}

	printf("unknown command '%s'\n", argv[2]);
	goto usage;
	return 0;
}

void error_msg(char *msg)
{
	printf("%s\nbe sure to check wiring, and check that pressure is applied on both sides of 360 Clip\n" \
		"sometimes it is required to move slightly the 360 Clip in case of a false contact\n", msg);
}

int read_id(unsigned char id[5])
{
	int i;
	unsigned char buf[5];

	GPIO_SET_1(COMMAND_LATCH_ENABLE);
	shortpause();
	GPIO_SET_0(N_WRITE_ENABLE);
	set_data_direction_out(); GPIO_DATA8_OUT(0x90); // Read ID byte 1
	shortpause();
	GPIO_SET_1(N_WRITE_ENABLE);
	shortpause(); set_data_direction_in();
	GPIO_SET_0(COMMAND_LATCH_ENABLE);
	shortpause();

	GPIO_SET_1(ADDRESS_LATCH_ENABLE);
	GPIO_SET_0(N_WRITE_ENABLE);
	set_data_direction_out(); GPIO_DATA8_OUT(0x00); // Read ID byte 2
	shortpause();
	GPIO_SET_1(N_WRITE_ENABLE);
	shortpause(); set_data_direction_in();
	GPIO_SET_0(ADDRESS_LATCH_ENABLE);
	shortpause();

	for (i = 0; i < 5; i++) {
		GPIO_SET_0(N_READ_ENABLE);
		shortpause();
		buf[i] = GPIO_DATA8_IN();
		GPIO_SET_1(N_READ_ENABLE);
		shortpause();
	}
	if (id != NULL)
		memcpy(id, buf, 5);
	else {
		printf("id = ");
		for (i = 0; i < 5; i++)
			printf("%02x ", buf[i]);
		printf("\n");
	}
	if (buf[0] == buf[1] && buf[1] == buf[2] && buf[2] == buf[3] && buf[3] == buf[4]) {
		error_msg("all five ID bytes are identical, this is not normal");
		return -1;
	}
	return 0;
}

inline int page_to_address(int page, int address_byte_index)
{
	switch(address_byte_index) {
	case 2:
		return page & 0xff;
	case 3:
		return (page >>  8) & 0xff;
	case 4:
		return (page >> 16) & 0xff;
	default:
		return 0;
	}
}

int send_command_address(int cmd1, int cmd2, int page)
{
	int i;

	set_data_direction_out();

	GPIO_SET_1(COMMAND_LATCH_ENABLE);
	shortpause(); GPIO_SET_0(N_WRITE_ENABLE);
	GPIO_DATA8_OUT(cmd1);
	shortpause(); GPIO_SET_1(N_WRITE_ENABLE);
	shortpause(); GPIO_SET_0(COMMAND_LATCH_ENABLE);
	shortpause();

	GPIO_SET_1(ADDRESS_LATCH_ENABLE);
	for (i = 0; i < 5; i++) {
		GPIO_SET_0(N_WRITE_ENABLE);
		GPIO_DATA8_OUT(page_to_address(page, i));
		shortpause();
		GPIO_SET_1(N_WRITE_ENABLE);
		shortpause();
	}
	GPIO_SET_0(ADDRESS_LATCH_ENABLE);
	shortpause();

	GPIO_SET_1(COMMAND_LATCH_ENABLE);
	shortpause(); GPIO_SET_0(N_WRITE_ENABLE);
	GPIO_DATA8_OUT(cmd2);
	shortpause(); GPIO_SET_1(N_WRITE_ENABLE);
	shortpause(); GPIO_SET_0(COMMAND_LATCH_ENABLE);
	shortpause();

	return 0;
}

int read_pages(int first_page_number, int number_of_pages, char *outfile, int write_spare)
{
	int page, i, n, retry_count;
	unsigned char id[5], id2[5];
	unsigned char buf[PAGE_SIZE * 2];
	FILE *badlog, *f = fopen(outfile, "w+");
	if (f == NULL) {
		perror("fopen output file");
		return -1;
	}
	if ((badlog = fopen("bad.log", "w+")) == NULL) {
		perror("fopen bad.log");
		return -1;
	}
	if (GPIO_READ(N_READ_BUSY) == 0) {
		error_msg("N_READ_BUSY should be 1 (pulled up), but reads as 0. make sure the NAND is powered on");
		return -1;
	}

	if (read_id(id) < 0)
		return -1;
	printf("NAND ID: ");
	for (i = 0; i < 5; i++) {
		printf("%02x ", id[i]);
	}
	printf("\nif this ID is incorrect, press Ctrl-C NOW to abort (3s timeout)\n");
	sleep(3);

	for (retry_count = 0, page = first_page_number*2; page < (first_page_number + number_of_pages)*2; page++) {
	retry:
		read_id(id2);
		if (memcmp(id, id2, 5) != 0) {
			error_msg("NAND ID has changed! make sure not to move the 360 Clip during operation. retrying\n");
			goto retry;
		}
		printf("reading page %d\n", page << 1);
		send_command_address(0x00, 0x30, page >> 1);
		for (i = 0; i < MAX_WAIT_READ_BUSY; i++) {
			if (GPIO_READ(N_READ_BUSY) == 0)
				break;
		}
		if (i == MAX_WAIT_READ_BUSY) {
			printf("N_READ_BUSY was not brought to 0 by NAND in time, retrying\n");
			goto retry;
		}
		set_data_direction_in();
		for (i = 0; i < MAX_WAIT_READ_BUSY; i++) {
			if (GPIO_READ(N_READ_BUSY) == 1)
				break;
		}
		if (i == MAX_WAIT_READ_BUSY) {
			printf("N_READ_BUSY was not brought to 1 by NAND in time, retrying\n");
			goto retry;
		}
		n = PAGE_SIZE*(page & 1);
		for (i = 0; i < PAGE_SIZE; i++) {
			GPIO_SET_0(N_READ_ENABLE);
			shortpause();
			buf[i + n] = GPIO_DATA8_IN();
			GPIO_SET_1(N_READ_ENABLE);
			shortpause();
		}
		if (!n) /* read the page again to ensure correct operation, bit 0 in page used for this purpose */
			continue;

		if (memcmp(buf, buf + PAGE_SIZE, PAGE_SIZE) != 0) {
			if (retry_count < 5) {
				printf("page failed to read correctly! retrying\n");
				retry_count++;
				page = page & ~1;
				goto retry;
			}
			printf("too many retries. perhaps bad block?\n");
			fprintf(badlog, "page %d seems to be bad\n", page >> 1);
			retry_count = 0;
		}
		if (write_spare) {
			if (fwrite(buf, PAGE_SIZE, 1, f) != 1) {
				perror("fwrite");
				return -1;
			}
		}
		else {
			if (fwrite(buf, 512 * (PAGE_SIZE / 512), 1, f) != 1) {
				perror("fwrite");
				return -1;
			}
		}
	}
}
Any help greatly appreciated!

Kaneda
Posts: 1
Joined: Mon Jan 07, 2019 12:14 pm

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Mon Jan 07, 2019 12:23 pm

As said before, there is a difference in nomenclature of the pins of the boards from 2012, GPIO00 became GPIO02 and GPIO01 GPIO03, I believe that errors are occurring in RPi3 because of this, try to change the pin reference from 0 to 2 and 1 to 3 in the program.

skypiece
Posts: 1
Joined: Fri Jan 01, 2016 9:37 am

Re: Bit-bangind a TSOP-48 NAND Flash with the RPi

Wed Jun 05, 2019 2:58 pm

Stoobs wrote:
Sat Dec 22, 2018 10:17 pm
Did anyone manage to get this working?
Yes. On modern Raspberries you may try to compile with

Code: Select all

g++ rpi-tsop48-nand-v1.c.c
.
Anyway, you need to include some header hiles, fix some old c language cases, etc.
I have done this recently. Github souces aviable here.

Return to “Interfacing (DSI, CSI, I2C, etc.)”