28fxxx: initial commit for new flash memory device (#2805)

Implementation of 28F010 and family flash memory devices. These are not compatible with the JEDEC-standard flash command protocol implemented in intelfsh.
This commit is contained in:
Patrick Mackinlay 2017-11-16 19:53:02 +07:00 committed by Vas Crabb
parent 62c9bcc660
commit f87cc5c671
4 changed files with 333 additions and 0 deletions

View File

@ -3301,3 +3301,15 @@ if (MACHINES["ADC0844"]~=null) then
MAME_DIR .. "src/devices/machine/adc0844.h",
}
end
---------------------------------------------------
--
--@src/devices/machine/28fxxx.h,MACHINES["28FXXX"] = true
---------------------------------------------------
if (MACHINES["28FXXX"]~=null) then
files {
MAME_DIR .. "src/devices/machine/28fxxx.cpp",
MAME_DIR .. "src/devices/machine/28fxxx.h",
}
end

View File

@ -619,6 +619,7 @@ MACHINES["I82586"] = true
MACHINES["INPUT_MERGER"] = true
-- MACHINES["K054321"] = true
MACHINES["ADC0844"] = true
MACHINES["28FXXX"] = true
--------------------------------------------------
-- specify available bus cores

View File

@ -0,0 +1,236 @@
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay
/*
* An implementation of the 28Fxxx flash memory devices, fabricated by many
* suppliers including Intel, Atmel, AMD, SGS, TI and Toshiba. Range includes:
*
* Part Bits Org.
* ------ ----- ------
* 28F256 256K 32Kx8
* 28F512 512K 64Kx8
* 28F010 1024K 128Kx8
* 28F020 2048K 256Kx8
* 28F040 4096K 512Kx8
*
* These devices use a JEDEC-standard pinout allowing them to be fitted to a
* standard EPROM socket, but have their own command set which is not
* compatible with the set implemented by the intelfsh devices. It appears the
* command set used here is a variation on the one defined by JEDEC standard
* 21-C, as "dual power supply eeprom command set", in figure 3.5.1-11 on page
* 23, here: https://www.jedec.org/system/files/docs/3_05_01R14.pdf
*
* This implementation has been developed primarily against Intel's datasheet,
* with some minor adjustments to match details in the AMD equivalent.
*
* TODO
* - implement more variants
* - testing in systems other than InterPro
*
*/
#include "emu.h"
#include "28fxxx.h"
#define VERBOSE 0
#include "logmacro.h"
// manufacturer codes defined by JEDEC: https://www.jedec.org/system/files/docs/JEP106AV.pdf
enum manufacturer_codes
{
MFG_AMD = 0x01,
MFG_INTEL = 0x89,
};
DEFINE_DEVICE_TYPE(INTEL_28F010, intel_28f010_device, "intel_28f010", "Intel 28F010 1024K (128K x 8) CMOS Flash Memory")
DEFINE_DEVICE_TYPE(AMD_28F010, amd_28f010_device, "amd_28f010", "Am28F010 1 Megabit (128K x 8-Bit) CMOS 12.0 Volt, Bulk Erase Flash Memory")
ALLOW_SAVE_TYPE(base_28fxxx_device::state);
base_28fxxx_device::base_28fxxx_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 size, u8 manufacturer_code, u8 device_code)
: device_t(mconfig, type, tag, owner, clock)
, device_nvram_interface(mconfig, *this)
, m_region(*this, DEVICE_SELF)
, m_size(size)
, m_manufacturer_code(manufacturer_code)
, m_device_code(device_code)
, m_program_power(CLEAR_LINE)
, m_state(STATE_READ_MEMORY)
{
assert_always((m_size & (m_size - 1)) == 0, "memory size must be an exact power of two");
}
intel_28f010_device::intel_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: base_28fxxx_device(mconfig, INTEL_28F010, tag, owner, clock, 0x20000, MFG_INTEL, 0xb4)
{
}
amd_28f010_device::amd_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: base_28fxxx_device(mconfig, AMD_28F010, tag, owner, clock, 0x20000, MFG_AMD, 0xa7)
{
}
void base_28fxxx_device::device_start()
{
m_data = std::make_unique<u8[]>(m_size);
save_item(NAME(m_program_power));
save_pointer(NAME(m_data.get()), m_size);
save_item(NAME(m_state));
save_item(NAME(m_address_latch));
}
void base_28fxxx_device::nvram_default()
{
if (m_region.found())
{
u32 bytes = m_region->bytes();
if (bytes > m_size)
bytes = m_size;
for (offs_t offs = 0; offs < bytes; offs++)
m_data[offs] = m_region->as_u8(offs);
}
else
erase();
}
void base_28fxxx_device::nvram_read(emu_file &file)
{
file.read(m_data.get(), m_size);
}
void base_28fxxx_device::nvram_write(emu_file &file)
{
file.write(m_data.get(), m_size);
}
void base_28fxxx_device::erase()
{
memset(m_data.get(), 0xff, m_size);
}
READ8_MEMBER(base_28fxxx_device::read)
{
switch (m_state)
{
case STATE_READ_MEMORY:
return m_data.get()[offset & (m_size - 1)];
case STATE_READ_IDENTIFIER:
switch (offset)
{
case 0: return m_manufacturer_code;
case 1: return m_device_code;
}
LOG("unknown read identifier offset 0x%08x (%s)\n", offset, machine().describe_context());
return space.unmap();
case STATE_ERASE_VERIFY:
return m_data.get()[m_address_latch];
case STATE_PROGRAM_VERIFY:
return m_data.get()[m_address_latch];
default:
LOG("unexpected read offset 0x%08x mask 0x%08x state %d (%s)\n", offset, mem_mask, m_state, machine().describe_context());
return space.unmap();
}
}
WRITE8_MEMBER(base_28fxxx_device::write)
{
// writes are ignored unless Vpp is asserted
if (!m_program_power)
return;
switch (m_state)
{
case STATE_ERASE_SETUP:
if (data == ERASE)
{
LOG("erase command initiated (%s)\n", machine().describe_context());
erase();
m_state = STATE_ERASE;
}
else if (data == RESET)
{
LOG("erase command reset (%s)\n", machine().describe_context());
m_state = STATE_READ_MEMORY;
}
else
LOG("unexpected command 0x%02x in erase setup state (%s)\n", data, machine().describe_context());
break;
case STATE_ERASE:
if (data == ERASE_VERIFY)
{
m_address_latch = offset & (m_size - 1);
m_state = STATE_ERASE_VERIFY;
}
else
LOG("unexpected command 0x%02x in erase state (%s)\n", data, machine().describe_context());
break;
case STATE_PROGRAM_SETUP:
m_address_latch = offset & (m_size - 1);
LOG("programming address 0x%08x data 0x%02x\n", m_address_latch, data);
// bits can only be written from uncharged (logical 1) to charged (logical 0) state
m_data.get()[m_address_latch] &= data;
m_state = STATE_PROGRAM;
break;
case STATE_PROGRAM:
if (data == PROGRAM_VERIFY)
m_state = STATE_PROGRAM_VERIFY;
else if (data == RESET)
{
LOG("program command reset (%s)\n", machine().describe_context());
m_state = STATE_READ_MEMORY;
}
else
LOG("unexpected command 0x%02x in program state (%s)\n", data, machine().describe_context());
break;
default:
// start a new command
switch (data)
{
case READ_MEMORY:
m_state = STATE_READ_MEMORY;
break;
case READ_IDENTIFIER:
case READ_IDENTIFIER_ALT:
m_state = STATE_READ_IDENTIFIER;
break;
case ERASE:
m_state = STATE_ERASE_SETUP;
break;
case ERASE_VERIFY:
m_address_latch = offset & (m_size - 1);
m_state = STATE_ERASE_VERIFY;
break;
case PROGRAM:
m_state = STATE_PROGRAM_SETUP;
break;
case RESET:
m_state = STATE_READ_MEMORY;
break;
default:
LOG("unexpected command 0x%02x in state %d (%s)\n", data, m_state, machine().describe_context());
break;
}
break;
}
}

View File

@ -0,0 +1,84 @@
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay
#ifndef MAME_MACHINE_28FXXX_H
#define MAME_MACHINE_28FXXX_H
#pragma once
class base_28fxxx_device : public device_t, public device_nvram_interface
{
public:
enum commands
{
READ_MEMORY = 0x00,
ERASE = 0x20,
PROGRAM = 0x40,
READ_IDENTIFIER_ALT = 0x80, // defined in AMD datasheet, but not Intel
READ_IDENTIFIER = 0x90,
ERASE_VERIFY = 0xa0,
PROGRAM_VERIFY = 0xc0,
RESET = 0xff
};
DECLARE_WRITE_LINE_MEMBER(vpp) { m_program_power = state; }
DECLARE_READ8_MEMBER(read);
DECLARE_WRITE8_MEMBER(write);
protected:
base_28fxxx_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 size, u8 manufacturer_code, u8 device_code);
virtual void device_start() override;
virtual void nvram_default() override;
virtual void nvram_read(emu_file &file) override;
virtual void nvram_write(emu_file &file) override;
optional_memory_region m_region;
private:
void erase();
// device specific parameters
const u32 m_size;
const u8 m_manufacturer_code;
const u8 m_device_code;
// accessible device state
int m_program_power;
std::unique_ptr<u8[]> m_data;
// internal state
enum state : u8
{
STATE_READ_MEMORY,
STATE_READ_IDENTIFIER,
STATE_ERASE_SETUP,
STATE_ERASE,
STATE_ERASE_RESET,
STATE_ERASE_VERIFY,
STATE_PROGRAM_SETUP,
STATE_PROGRAM,
STATE_PROGRAM_VERIFY
};
state m_state;
u32 m_address_latch;
};
class intel_28f010_device : public base_28fxxx_device
{
public:
intel_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
};
class amd_28f010_device : public base_28fxxx_device
{
public:
amd_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
};
DECLARE_DEVICE_TYPE(INTEL_28F010, intel_28f010_device)
DECLARE_DEVICE_TYPE(AMD_28F010, amd_28f010_device)
#endif // MAME_MACHINE_28FXXX_H