GEN-0001 - Memory Check per 4464 e 41464

Questo articolo è nato durante l'esigenza di riparare un Sinclair Spectrum +2B (leggete la storia completa all'indirizzo SSP2-0001 - Spectrum +2B - Riparazione e diagnosi con Arduino 5260 ) che aveva un probabile guasto alla RAM.

La RAM indagata era del tipo 41464 e di questa ho studiato funzionamento e datasheet.

Memorie DRAM:
Prima di tutto intendiamoci su una cosa: le memorie del tipo 4464 e 41464 sono delle DRAM, ovvero Dynamic Random Access Memory. Si tratta di una tipologia di memorie RAM che immagazzina ogni bit in un condensatore dedicato. Il livello di carica del condensatore determina se il bit vale 1 oppure 0. Se il condensatore perde la carica, l'informazione è perduta e per questo motivo deve essere attivato un processo periodico di ricarica. Per questo motivo sono anche dette memorie dinamiche, in contrapposizione con le memorie statiche SRAM. Tale caratteristica di perdere l'informazione a 1 dopo un certo tempo, fa sì che vengano anche definite memorie volatili.

Ma torniamo ora ai chip di memoria 41464 che sospettavo essere rotti (tutti o in parte). Queste DRAM sono da 64K × 4 bit , cioè contengono 32 KB per chip. Sono quindi necessari due di questi chip insieme per fare 64K di memoria ed avere a disposizione tutti gli 8 bit di dati.

Definizione di parola di memoria:
Per i non addetti ai lavori, vediamo come è realizzata una memoria DRAM. I dispositivi di memoria primaria a semiconduttore sono internamente organizzati in parole (word). Una parola di memoria è il minimo gruppo di bit che è possibile leggere o scrivere in memoria.

Facendo una similitudine, è come se la memoria fosse una grande cassettiera suddivisa in molti cassetti. Ogni cassetto rappresenta una parola che è a sua volta composta da un certo numero di bit. Ogni bit è come se fosse uno scompartimento del cassetto.


Le memorie di tipo 4464 e 41464 sono composte da 65.536 parole da 4 bit e sono sviluppate con tecnologia Silicon Gate (giusto per la cronaca, la tecnologia Silicon Gate è quella che ha permesso lo sviluppo dei microprocessori e delle memorie a partire dagli anni 80' ed è stata inventata dal genio italiano Federico Faggin, quello che ha inventato e prodotto lo Z80)

Vediamo a sinistra il pinout di queste memorie. I pin da O1 a O4 servono per i dati (e sono Input/Output in quanto possiamo sia leggere sia scrivere).

I pin da A0 ad A7 servono per gli indirizzi e sono ovviamente un Input per il Chip. Ma se le memorie sono da 64K come è possibile che i piedini per gli indirizzi siano solo 8 (con conseguente possibilità di indirizzare solo 256 'cassetti')? Il trucco è questo: per indirizzare una cella occorre prima selezionare la Riga (256 righe) mediante il segnale RAS attivo basso e successivamente la Colonna (256 colonne) mediante il segnale CAS anche questo attivo basso. Magicamente abbiamo 256x256 celle ovvero 64K parole da 4 bit.

Quindi il paragone con la cassettiera non era poi così "fedele" : qui abbiamo praticamente 256 cassettiere (colonne) che hanno 256 cassetti ciascuna (righe). Per aprire un cassetto devo indicare prima la riga e la colonna.

Vedremo successivamente come leggere e scrivere la memoria. Non scordiamoci poi che, come accennato all'inizio, questo tipo di memorie vanno "rinfrescate" se vogliamo che l'informazione non si perda dopo qualche istante a causa della scarica dei condensatori.

Dopo avere preso spunto da vari progetti di tipo simile ho prototipato basetta il primo circuito e ho iniziato a scrivere e ottimizzare il codice per eseguire i clicli di scrittura e lettura delle memorie per verificarne il corretto funzionamento.


Ecco il risultato finale ... carino vero?

Di seguito trovate il codice sorgente funzionante ma ancora da ottimizzare.

Source code

/**

DRAM41464-4464_Checker v1.0

Copyright (C) 2020 Davide Torre (datorr@tiscali.it)


DRAM 41464 , 4464 tester for Arduino Mega 5260


Inspired from base code Niteto ChipChecker by Dominik Tonn


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 "stdio.h"


#define Version "v1.0"


/*

Chip Pinout:


OE (NEG) ---> | PIN1 PIN18 |<-- Vss

O1 <--------> | PIN2 PIN17 |<-> O4

O2 <--------> | PIN3 PIN16 |<-- CAS (NEG)

WRITE (NEG)-> | PIN4 PIN15 |<-> O3

RAS (NEG) --> | PIN5 PIN14 |<-- A0

A6 ---------> | PIN6 PIN13 |<-- A1

A5 ---------> | PIN7 PIN12 |<-- A2

A4 ---------> | PIN8 PIN11 |<-- A3

VCC --------> | PIN9 PIN10 |<-- A7

*/


/**

Addresses Pinout and corresponding PIN on Arduino Mega 5260

*/

#define A0_PIN14 42 // A0 (Chip PIN 14 mapped on Arduino Pin 42)

#define A1_PIN13 43 // A1 (Chip PIN 13 mapped on Arduino Pin 43)

#define A2_PIN12 44 // A2 (Chip PIN 12 mapped on Arduino Pin 44)

#define A3_PIN11 45 // A3 (Chip PIN 11 mapped on Arduino Pin 45)

#define A4_PIN08 46 // A4 (Chip PIN 08 mapped on Arduino Pin 46)

#define A5_PIN07 47 // A5 (Chip PIN 07 mapped on Arduino Pin 47)

#define A6_PIN06 48 // A6 (Chip PIN 06 mapped on Arduino Pin 48)

#define A7_PIN10 49 // A7 (Chip PIN 10 mapped on Arduino Pin 49)

/**

Data Pinout and corresponding PIN on Arduino Mega 5260

*/

#define O1_PIN02 30 // O1 (Chip PIN 02 mapped on Arduino Pin 30)

#define O2_PIN03 31 // O2 (Chip PIN 03 mapped on Arduino Pin 31)

#define O3_PIN15 32 // O3 (Chip PIN 15 mapped on Arduino Pin 32)

#define O4_PIN17 33 // O4 (Chip PIN 17 mapped on Arduino Pin 33)


/**

Control

*/

#define RAS 52

#define _OE 51

#define _W 50

#define CAS 53


/**

* Address array

*/

uint8_t addr[8] {

A0_PIN14,

A1_PIN13,

A2_PIN12,

A3_PIN11,

A4_PIN08,

A5_PIN07,

A6_PIN06,

A7_PIN10

};



String inputString = "";

String cmd, param;

uint32_t tRefresh;


void setupDram() {

pinMode(CAS, OUTPUT); digitalWrite(CAS, HIGH);

pinMode(RAS, OUTPUT); digitalWrite(RAS, HIGH);

pinMode(_W, OUTPUT); digitalWrite(_W, HIGH);

pinMode(_OE, OUTPUT); digitalWrite(_OE, HIGH);

for (int i = 0; i < 8; i++) {

pinMode(addr[i], OUTPUT);

/**

Vedi Sheet samsung dice che servono 8 cicli RAS perchè funzioni correttamente

*/

pinMode(RAS, OUTPUT); digitalWrite(RAS, LOW);

delay(500);

pinMode(RAS, OUTPUT); digitalWrite(RAS, HIGH);


}

}


/**

Set the address on pins A0..A7

*/

void setAddr(uint8_t address) {

for (int i = 0; i < 8; i++) {

digitalWrite(addr[i], address & 0x01);

address = address >> 1;

}

}


// reads from <row>, <col>

// returns 4bits

uint8_t readAddr(uint8_t row, uint8_t col) {

pinMode(O1_PIN02, INPUT_PULLUP);

pinMode(O2_PIN03, INPUT_PULLUP);

pinMode(O3_PIN15, INPUT_PULLUP);

pinMode(O4_PIN17, INPUT_PULLUP);

setAddr(row);

digitalWrite(RAS, LOW);

digitalWrite(_W, HIGH);

setAddr(col);

digitalWrite(CAS, LOW);

digitalWrite(_OE, LOW);

uint8_t data = digitalRead(O4_PIN17) * 8 + digitalRead(O3_PIN15) * 4 + digitalRead(O2_PIN03) * 2 + digitalRead(O1_PIN02) * 1;

digitalWrite(CAS, HIGH);

digitalWrite(RAS, HIGH);

digitalWrite(_OE, HIGH);

return data;

}


// write lower nibble of <data> to <row>, <col>

void writeAddr(uint8_t row, uint8_t col, uint8_t data) {

pinMode(O1_PIN02, OUTPUT);

pinMode(O2_PIN03, OUTPUT);

pinMode(O3_PIN15, OUTPUT);

pinMode(O4_PIN17, OUTPUT);

setAddr(row);

digitalWrite(RAS, LOW);

digitalWrite(_W, LOW);

//delay(1000);

digitalWrite(O4_PIN17, (data >> 3) & 0x01);

digitalWrite(O3_PIN15, (data >> 2) & 0x01);

digitalWrite(O2_PIN03, (data >> 1) & 0x01);

digitalWrite(O1_PIN02, (data) & 0x01);


setAddr(col);


digitalWrite(CAS, LOW);

digitalWrite(_W, HIGH);

digitalWrite(CAS, HIGH);

digitalWrite(RAS, HIGH);


pinMode(O1_PIN02, INPUT_PULLUP);

pinMode(O2_PIN03, INPUT_PULLUP);

pinMode(O3_PIN15, INPUT_PULLUP);

pinMode(O4_PIN17, INPUT_PULLUP);


}


// tests for stuck databus IO lines

boolean testDatabus() {

boolean fail = false;

uint8_t comp;

for (int i = 0; i < 16; i++) {

writeAddr(0, 0, i);

comp = readAddr(0, 0);

if (comp != i) fail = true;

}

if (fail) {

writeAddr(0, 0, 0x0f);

uint8_t stuckLow = (~readAddr(0, 0)) & 0x0f;

writeAddr(0, 0, 0x00);

uint8_t stuckHigh = readAddr(0, 0) & 0x0f;

if (stuckLow != 0) {

Serial.print("IO Lines[1..4] stuck Low: ");

for (int i = 0; i < 4; i++) {


if ((stuckLow & 0x01) == 1) {

Serial.print("IO[");

Serial.print(i + 1);

Serial.print("]");

}

stuckLow = stuckLow >> 1;

}

Serial.println();

}

if (stuckHigh != 0) {

Serial.print("IO Lines[1..4] stuck High: ");

for (int i = 0; i < 4; i++) {

if ((stuckHigh & 0x01) == 1) {

Serial.print("IO[");

Serial.print(i + 1);

Serial.print("]");

}

stuckHigh = stuckHigh >> 1;

}

Serial.println();

}

}

return !fail;

}



/**

testAddressbus

Tests for stuck addess bus lines

*/

int16_t testAddressBus() {

uint8_t value10 = 0x0a;

uint8_t value05 = 0x05;


for (uint16_t i = 1; i < 256; i <<= 1) {

writeAddr(i, 0, value10);

}

writeAddr(0, 0, value05);


for (uint16_t i = 1; i < 256; i <<= 1) {

uint8_t readvalue = readAddr(i, 0);

if (readvalue != value10) {

Serial.print("atteso ");

Serial.print(value10);

Serial.print(" ma trovato ");

Serial.print(readvalue);

return i;

}

}

writeAddr(0, 0, value10);


for (uint16_t i = 1; i < 256; i <<= 1) {

writeAddr(i, 0, value05);

if (readAddr(0, 0) != value10) return i;

for (uint16_t j = 1; j < 256; j <<= 1)

if ((readAddr(j, 0) != value10) && j != i) return i;

writeAddr(i, 0, value10);

}

/**

If the method returns -1 it means the test is OK!!

*/

return -1;

}


/*

Complete memory test.

The method returns the number of corrupted cells.

If return value == 0 then the memory chip is ok

*/

long testMem() {

long cellsok = 0;

long cellsko = 0;

//int numrows = 256; // Default is 256 (total number of cols)

int numrows = 256;

for (uint16_t row = 0; row < numrows; row++) {

if (row % 32 == 0) {

if (row != 0) {

Serial.println();

}

Serial.print((String)"Testing : 0x" + row * 256 + ".4x-0x" + (row * 256 + 512 * 16 - 1) + ".4x");

Serial.print((String)"\nReport: Row:" + row + " - Read/Write Operations=" + (cellsok + cellsko) + ", OK=" + cellsok + " - KO:" + cellsko);


}

else Serial.print(".");

//Serial.print((String)"\nReport: Row:" + row + " - Read/Write Operations=" + (cellsok+cellsko) + ", OK=" + cellsok + " - KO:" + cellsko);


//int numcols = 256; // Default

int numcols = 256;

for (uint8_t pattern = 0; pattern < 16; pattern++) {

for (uint16_t col = 0; col < numcols; col++) {

writeAddr(row, col, pattern);

}

for (uint16_t col = 0; col < numcols; col++) {

uint8_t valoreletto = readAddr(row, col);

if (valoreletto != pattern) {

cellsko++;

//Serial.print((String)"\nFAIL [R=" + row + ",C=" + col + "] - Read:" + valoreletto + " - Exp:" + pattern);

/*

Serial.print(" - ledcol:");

Serial.print(col / 32);

Serial.print(" - ledrow:");

Serial.print(row / 32);

lc.setLed(0, col / 32, row / 32, true);

*/

} else {

cellsok++;

}

}

}

}

Serial.println();

Serial.print((String)"\nFinal Report: Read/Write Transactions=" + (cellsok + cellsko) + ", OK=" + cellsok + " - KO:" + cellsko);


return cellsko;

}





long customMem() {

uint8_t row = 10;

uint8_t col = 200;

uint8_t value = 2;

uint8_t ritorno = -1;

Serial.print((String)"\nScrivo [R=" + row + ",C=" + col + "]=" + value);

writeAddr(row, col, value);

uint8_t valoreletto = readAddr(row, col);

Serial.print((String)"\nLeggo [R=" + row + ",C=" + col + "]=" + valoreletto);

if (valoreletto != value) {

Serial.print((String)"\nFAIL [R=" + row + ",C=" + col + "] - Read:" + valoreletto + " - Exp:" + value);

ritorno = 0;

} else {

Serial.print((String)"\nOK!!! [R=" + row + ",C=" + col + "] - Read:" + valoreletto + " - Exp:" + value);

}

return ritorno;

}


// refresh <row>

void refresh(uint8_t row) {

setAddr(row);

digitalWrite(RAS, LOW);

digitalWrite(RAS, HIGH);

}


/*

Reads command from the serial port

*/

void getCommandFromSerialPort() {

inputString = Serial.readString();

}


/*

execute command

*/

void doCommand() {

inputString.replace((char)10, (char)0);

inputString.replace((char)13, (char)0);

Serial.println(inputString);

cmd = inputString;

param = inputString;

if (inputString.indexOf(" ") > 0) {

cmd.remove(inputString.indexOf(" "), inputString.length());

param.remove(0, inputString.indexOf(" "));

} else {

cmd = inputString;

param = "";

}

cmd.trim();

param.trim();


if (cmd == "full") {

Serial.print("Command detected : ");

Serial.println(cmd);

Serial.print("Parameter : ");

Serial.println(param);


Serial.print("Testing DataBus: ");

if (testDatabus()) Serial.println("OK");

else Serial.println("FAIL");


Serial.print("Testing AddressBus: ");

int16_t result = testAddressBus();

if (result == -1) Serial.println("OK");

else {

Serial.print("FAIL at ");

Serial.println(result);

}


//Serial.println("Testing Memory:");

//result = testMem();

//if (result == -1) Serial.println("Memory OK");

//else Serial.println("Memory FAIL");

}

if (cmd == "data") {

Serial.print("Command detected : ");

Serial.println(cmd);

Serial.print("Parameter : ");

Serial.println(param);

Serial.print("Testing DataBus: ");

if (testDatabus()) Serial.println("OK");

else Serial.println("FAIL");

}

if (cmd == "adr") {

Serial.print("Command detected : ");

Serial.println(cmd);

Serial.print("Parameter : ");

Serial.println(param);


Serial.print("Testing AddressBus: ");

int16_t result = testAddressBus();

if (result == -1) Serial.println("OK");

else {

Serial.print("FAIL at: ");

Serial.println(result);

}

}

if (cmd == "mem") {

Serial.print("Command detected : "); Serial.println(cmd);

Serial.print("Parameter : "); Serial.println(param);


Serial.println("Testing Memory in progress");

long result = testMem();

if (result == 0) {

Serial.println("\nMemory OK");

} else {

Serial.println("\nMemory FAIL");

}

}

if (cmd == "cust") {

Serial.print("Command detected : "); Serial.println(cmd);

Serial.print("Parameter : "); Serial.println(param);


Serial.println("Testing Custom in progress");

long result = customMem();

if (result == 0) {

Serial.println("\nMemory OK");

} else {

Serial.println("\nMemory FAIL");

}

}



}



void setup() {

setupDram();

Serial.begin(115200);

delay(1000);

inputString.reserve(200);

cmd.reserve(20);

param.reserve(20);


Serial.println("data - tests data bus for stuck lines");

Serial.println("adr - tests address bus for stuck lines");

Serial.println("mem - tests full memory range");

Serial.println("full - databus, address bus and full memory test");

Serial.println("cust - custom test");


delay(2000);


}


void loop() {

if (Serial.available() != 0) {

getCommandFromSerialPort();

doCommand();

}

}

Data articolo: 8 gennaio 2020
Ultima modifica : 17 gennaio 2020