From 3ae4829ab56b5c28f1b25863ebffb4c44d03fb46 Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Wed, 8 Jan 2025 13:03:04 +0700 Subject: vme/enp10: improve to working state [Patrick Mackinlay, Plamen Mihaylov] * added missing 2ms timer * improved interrupt logic * corrected and completed vme interface * added additional firmware versions --- src/devices/bus/vme/enp10.cpp | 333 +++++++++++++++++++++++++++++++++--------- src/devices/bus/vme/enp10.h | 29 +++- 2 files changed, 285 insertions(+), 77 deletions(-) diff --git a/src/devices/bus/vme/enp10.cpp b/src/devices/bus/vme/enp10.cpp index e0406f1f91f..1dee2568883 100644 --- a/src/devices/bus/vme/enp10.cpp +++ b/src/devices/bus/vme/enp10.cpp @@ -4,33 +4,54 @@ /* * Communication Machinery Corporation Ethernet Node Processor (ENP-10) * + * Rebadged/resold by Motorola as the MVME330 Ethernet Controller, and also + * by Silicon Graphics as part 013-0204-00[123]. + * + * Firmware expects to find a UART at address 0xef'8010-0xef'802f, which was + * apparently provided by a Mizar VME8300 card. + * * Sources: * - Ethernet Node Processor ENP-30 Reference Guide (6213000-05B), Communication Machinery Corporation, November 15, 1988 + * - MVME330 Ethernet Controller User's Manual (MVME330/D2), Motorola, Second Edition, 1988 * * TODO: - * - verify registers - * - uart? - */ - -/* - * WIP - * --- - * - 0xef'8010-0xef'802f appears to be a uart, but doesn't match scn2681 per ENP-30 documentation? - * - following text is output to 0xef'8025 at startup: - * CMC ENP/10 CMOS - 112708 Bytes Free - * Ethernet Address: 02CF1F123456 - * Allocating 30 receive buffers - * Allocating 30 transmit buffers + * - remaining control register flags + * - configurable interrupts + * - MVME330 -1 and -2 variants */ #include "emu.h" #include "enp10.h" -#define VERBOSE 0 +//#define VERBOSE (LOG_GENERAL) #include "logmacro.h" DEFINE_DEVICE_TYPE(VME_ENP10, vme_enp10_card_device, "enp10", "CMC ENP-10") +enum csr_mask : u8 +{ + CSR_SUSPEND = 0x08, // set by lance dma + CSR_TIMER = 0x10, // set by 2ms timer + CSR_RESET = 0x20, // assert vme reset + CSR_FAIL = 0x40, + CSR_VIRQ = 0x80, +}; +enum obr_mask : u8 +{ + OBR_IE = 0x01, // interrupt enable + OBR_TI = 0x02, // transmit interrupt + OBR_RI = 0x04, // receive interrupt + OBR_UI = 0x08, // utility interrupt + OBR_RE = 0x80, // RAM enable +}; +enum exr_mask : u8 +{ + EXR_RTO = 0x01, // resource time-out + EXR_PER = 0x02, // parity error + EXR_ABO = 0x04, // abort + EXR_ACLO = 0x08, // AC line voltage low +}; + vme_enp10_card_device::vme_enp10_card_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : device_t(mconfig, VME_ENP10, tag, owner, clock) , device_vme_card_interface(mconfig, *this) @@ -43,15 +64,29 @@ vme_enp10_card_device::vme_enp10_card_device(machine_config const &mconfig, char } ROM_START(enp10) - ROM_REGION16_BE(0x4000, "eprom", 0) + ROM_REGION16_BE(0x10000, "eprom", 0) ROM_SYSTEM_BIOS(0, "enp10_0", "CMC ENP/10 CMOS") ROMX_LOAD("link_10_2.0_nh_rev.4.1h.u4", 0x0000, 0x2000, CRC(7532f2b1) SHA1(bdef6c525f451fbc67f3d4625c9db18975e7e1e4), ROM_SKIP(1) | ROM_BIOS(0)) ROMX_LOAD("link_10_2.0_nh_rev.k4.1l.u3", 0x0001, 0x2000, CRC(f2decb78) SHA1(795623274bfff6273790c30445e4dca4064859ed), ROM_SKIP(1) | ROM_BIOS(0)) + ROM_FILL(0x4000, 0xc000, 0xff) ROM_SYSTEM_BIOS(1, "enp10_1", "CMC ENP/10 CMOS (SGI?)") ROMX_LOAD("8845__070_0132_002s.u4", 0x0000, 0x2000, CRC(3ea05f63) SHA1(ee523928d27b854cd1be7e6aa2b8bb093d240022), ROM_SKIP(1) | ROM_BIOS(1)) ROMX_LOAD("8845__070_0131_002s.u3", 0x0001, 0x2000, CRC(d4439fb9) SHA1(51466000b613ab5c03b2bf933e1a485fe2e53d04), ROM_SKIP(1) | ROM_BIOS(1)) + ROM_FILL(0x4000, 0xc000, 0xff) + + ROM_SYSTEM_BIOS(2, "mvme330", "MVME330") + ROMX_LOAD("knlrom10__v4.1-h.u4", 0x0000, 0x2000, CRC(b5f0a49b) SHA1(70e0d54c25a152503796fae8d7c5ffab6d625583), ROM_SKIP(1) | ROM_BIOS(2)) + ROMX_LOAD("knlrom10__v4.1-l.u3", 0x0001, 0x2000, CRC(78d8ae1b) SHA1(c69a8fa2edec7d6faadb48591ce252ac45b55cad), ROM_SKIP(1) | ROM_BIOS(2)) + ROM_FILL(0x4000, 0xc000, 0xff) + + // this firmware requires 512KiB RAM + ROM_SYSTEM_BIOS(3, "mvme330_1", "MVME330-1") + ROMX_LOAD("u_rev-99592-616335-2.u4", 0x0000, 0x8000, CRC(88527c5e) SHA1(b953d99d5eb3462c41202f64f560fae592e269fa), ROM_SKIP(1) | ROM_BIOS(3)) + ROM_CONTINUE(0x0000, 0x8000) // first 32K is 0xff + ROMX_LOAD("u_rev-99592-616335-1.u3", 0x0001, 0x8000, CRC(0d8e5aa4) SHA1(f4b165581c5840e0607bd979c297d3e5ee77cb0f), ROM_SKIP(1) | ROM_BIOS(3)) + ROM_CONTINUE(0x0001, 0x8000) // first 32K is 0xff // hand-crafted prom containing address 02:cf:1f:12:34:56 ROM_REGION16_BE(0x20, "mac", 0) @@ -83,13 +118,16 @@ void vme_enp10_card_device::device_start() save_item(NAME(m_ivr)); save_item(NAME(m_csr)); - save_item(NAME(m_ier)); - save_item(NAME(m_tir)); - save_item(NAME(m_rir)); - save_item(NAME(m_uir)); - save_item(NAME(m_rer)); + save_item(NAME(m_obr)); save_item(NAME(m_exr)); - save_item(NAME(m_hir)); + + save_item(NAME(m_bint)); + save_item(NAME(m_lint)); + save_item(NAME(m_int_state)); + + m_bint = false; + m_lint = false; + m_int_state = 0; } void vme_enp10_card_device::device_reset() @@ -97,14 +135,9 @@ void vme_enp10_card_device::device_reset() m_boot.select(0); m_ivr = 0; - m_csr = 0; - m_ier = 0; - m_tir = 0; - m_rir = 0; - m_uir = 0; - m_rer = 0; + m_csr = CSR_FAIL | CSR_RESET; + m_obr = 0; m_exr = 0; - m_hir = 0; u32 const base = m_base->read() << 16; @@ -112,110 +145,272 @@ void vme_enp10_card_device::device_reset() vme_space(vme::AM_3d).install_device(base, base | 0x1'ffff, *this, &vme_enp10_card_device::vme_map); vme_irq_w<4>(1); + + interrupt(); } void vme_enp10_card_device::device_add_mconfig(machine_config &config) { M68000(config, m_cpu, 20_MHz_XTAL / 2); m_cpu->set_addrmap(AS_PROGRAM, &vme_enp10_card_device::cpu_map); + m_cpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &vme_enp10_card_device::cpu_ack); AM7990(config, m_net, 20_MHz_XTAL / 2); - m_net->intr_out().set_inputline(m_cpu, INPUT_LINE_IRQ6).invert(); + m_net->intr_out().set( + [this](int state) + { + m_lint = !state; + + interrupt(); + }); m_net->dma_in().set([this](offs_t offset) { return m_cpu->space(0).read_word(offset); }); m_net->dma_out().set([this](offs_t offset, u16 data, u16 mem_mask) { m_cpu->space(0).write_word(offset, data, mem_mask); }); + TIMER(config, "timer").configure_periodic(FUNC(vme_enp10_card_device::timer), attotime::from_msec(2)); + vme_iack().set(FUNC(vme_enp10_card_device::iack_r)); + vme_berr().set( + [this](int state) + { + m_exr |= EXR_RTO; + + m_cpu->trigger_bus_error(); + }); } void vme_enp10_card_device::cpu_map(address_map &map) { + map(0x00'1000, 0xee'ffff).rw(FUNC(vme_enp10_card_device::vme_a24_r), FUNC(vme_enp10_card_device::vme_a24_w)); + map(0xef'0000, 0xef'ffff).rw(FUNC(vme_enp10_card_device::vme_a16_r), FUNC(vme_enp10_card_device::vme_a16_w)); + map(0xf0'0000, 0xf1'ffff).ram().share("ram"); - map(0xf8'0000, 0xf8'3fff).rom().region("eprom", 0).mirror(0x02'0000); + map(0xf8'0000, 0xf8'ffff).rom().region("eprom", 0).mirror(0x02'0000); map(0x00'0000, 0xf1'ffff).view(m_boot); - // map first 1k of eprom at 0x00'0000 + // map first 4k of eprom at 0x00'0000 m_boot[0](0x00'0000, 0x00'0fff).rom().region("eprom", 0); - // map first 1k of ram at 0x00'0000, unmap at 0xf0'0000 + // map first 4k of ram at 0x00'0000, unmap at 0xf0'0000 m_boot[1](0x00'0000, 0x01'ffff).ram().share("ram"); m_boot[1](0x00'1000, 0x01'ffff).unmaprw(); m_boot[1](0xf0'0000, 0xf0'0fff).unmaprw(); - // uart: 16 byte registers 10-2f? - map(0xef'8010, 0xef'802f).noprw(); - - map(0xfe'0080, 0xfe'0081).umask16(0x00ff).rw(FUNC(vme_enp10_card_device::addr_r), FUNC(vme_enp10_card_device::irq_w)); + map(0xfe'0080, 0xfe'0081).mirror(0x1e).umask16(0x00ff).rw(FUNC(vme_enp10_card_device::vect_r), FUNC(vme_enp10_card_device::vect_w)); - map(0xfe'00a0, 0xfe'00a1).umask16(0x00ff).lrw8( + map(0xfe'00a0, 0xfe'00a1).mirror(0x1e).umask16(0x00ff).lrw8( [this]() { return m_csr; }, "csr_r", [this](u8 data) { LOG("csr_w 0x%02x\n", data); - m_led[0] = BIT(data, 6); // fail? - m_led[1] = BIT(data, 5); // run? + m_led[0] = BIT(data, 6); // fail + m_led[1] = !BIT(data, 6); // run - m_csr = data; + // TODO: CSR_RESET + // TODO: CSR_SUSPEND + m_csr = (m_csr & (CSR_VIRQ | CSR_TIMER)) | (data & (CSR_FAIL | CSR_RESET)); }, "csr_w"); - map(0xfe'00c0, 0xfe'00c1).umask16(0x00ff).lrw8(NAME([this]() { return m_ier; }), NAME([this](u8 data) { m_ier = data; interrupt(); })); - map(0xfe'00c2, 0xfe'00c3).umask16(0x00ff).lrw8(NAME([this]() { return m_tir; }), NAME([this](u8 data) { m_tir = data; interrupt(); })); - map(0xfe'00c4, 0xfe'00c5).umask16(0x00ff).lrw8(NAME([this]() { return m_rir; }), NAME([this](u8 data) { m_rir = data; interrupt(); })); - map(0xfe'00c6, 0xfe'00c7).umask16(0x00ff).lrw8(NAME([this]() { return m_uir; }), NAME([this](u8 data) { m_uir = data; interrupt(); })); - map(0xfe'00ce, 0xfe'00cf).umask16(0x00ff).lrw8(NAME([this]() { return m_rer; }), NAME([this](u8 data) { m_rer = data; m_boot.select(BIT(data, 7)); })); + map(0xfe'00c0, 0xfe'00cf).mirror(0x10).umask16(0x00ff).rw(FUNC(vme_enp10_card_device::obr_r), FUNC(vme_enp10_card_device::obr_w)); - map(0xfe'00e0, 0xfe'00e1).umask16(0x00ff).lrw8(NAME([this]() { return m_exr; }), NAME([this](u8 data) { m_exr = data; })); // TODO: bit 1 parity error? - map(0xfe'00ee, 0xfe'00ef).umask16(0x00ff).lr8(NAME([]() { return 0; })); // TODO: bit 1 enables additional ram test? + map(0xfe'00e0, 0xfe'00ef).mirror(0x10).umask16(0x00ff).lrw8( + NAME([this]() { return m_exr; }), + NAME([this](u8 data) + { + if (m_exr) + LOG("exr_w 0x%02x (%s)\n", data, machine().describe_context()); + + m_exr = 0; + m_csr &= ~CSR_TIMER; - map(0xfe'0200, 0xfe'0203).rw(m_net, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w)); - map(0xfe'0400, 0xfe'041f).rom().region("mac", 0); + interrupt(); + })); - // TODO: verify the next two registers - map(0xfe'0e00, 0xfe'0e01).umask16(0x00ff).lrw8(NAME([this]() { return m_hir; }), NAME([this](u8 data) { m_hir = data; interrupt(); })); - map(0xfe'0f00, 0xfe'0fff).umask16(0x00ff).lrw8(NAME([this]() { reset(); return 0; }), NAME([this](u8 data) { reset(); })); + map(0xfe'0200, 0xfe'0203).mirror(0x1fc).rw(m_net, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w)); + map(0xfe'0400, 0xfe'041f).mirror(0x3e0).rom().region("mac", 0); } void vme_enp10_card_device::vme_map(address_map &map) { - map(0x0'0000, 0x1'efff).lrw16( + map(0x0'0000, 0x1'fdff).lrw16( [this](offs_t offset, u16 mem_mask) { return m_cpu->space(0).read_word(0xf0'0000 | (offset << 1), mem_mask); }, "mem_r", [this](offs_t offset, u16 data, u16 mem_mask) { m_cpu->space(0).write_word(0xf0'0000 | (offset << 1), data, mem_mask); }, "mem_w"); - map(0x1'f000, 0x1'ffff).lrw16( - [this](offs_t offset, u16 mem_mask) { return m_cpu->space(0).read_word(0xfe'0000 | (offset << 1), mem_mask); }, "reg_r", - [this](offs_t offset, u16 data, u16 mem_mask) { m_cpu->space(0).write_word(0xfe'0000 | (offset << 1), mem_mask); }, "reg_w"); + map(0x1'fe00, 0x1'feff).lw16( + [this](u16 data) + { + LOG("host interrupt (%s)\n", machine().describe_context()); + + m_bint = true; + interrupt(); + }, "bint_w"); + + map(0x1'ff00, 0x1'ffff).lw16( + [this](u16 data) + { + LOG("host reset (%s)\n", machine().describe_context()); + reset(); + }, "reset_w"); } -u8 vme_enp10_card_device::addr_r() +u8 vme_enp10_card_device::vect_r() { - LOG("addr_r (%s)\n", machine().describe_context()); + LOG("vect_r (%s)\n", machine().describe_context()); - // TODO: what is returned here? return m_base->read(); } -void vme_enp10_card_device::irq_w(u8 data) +void vme_enp10_card_device::vect_w(u8 data) { - LOG("irq 0x%02x (%s)\n", data, machine().describe_context()); + LOG("vect_w 0x%02x (%s)\n", data, machine().describe_context()); - m_ivr = data; + if (!(m_csr & CSR_VIRQ)) + { + m_ivr = data; + m_csr |= CSR_VIRQ; - vme_irq_w<4>(0); + vme_irq_w<4>(0); + } } u8 vme_enp10_card_device::iack_r() { - vme_irq_w<4>(1); + if (m_csr & CSR_VIRQ) + { + vme_irq_w<4>(1); + m_csr &= ~CSR_VIRQ; + } return m_ivr; } +u8 vme_enp10_card_device::obr_r(offs_t offset) +{ + return (m_obr & (1U << offset)) ? 0x80 : 0; +} + +void vme_enp10_card_device::obr_w(offs_t offset, u8 data) +{ + unsigned const state = BIT(data, 7); + + if (BIT(m_obr, offset) != state) + { + static char const *const reg[] = { "ier", "tir", "rir", "uir", "bit4", "bit5", "bit6", "mapr" }; + //LOG("obr_w %s %u (%s)\n", reg[offset], state, machine().describe_context()); + + if (state) + m_obr |= 1U << offset; + else + m_obr &= ~(1U << offset); + + if (offset < 4) + interrupt(); + else if (offset == 7) + m_boot.select(state); + } +} + +void vme_enp10_card_device::timer(timer_device &timer, s32 param) +{ + m_csr |= CSR_TIMER; + + interrupt(); +} + void vme_enp10_card_device::interrupt() { - bool const enable = BIT(m_ier, 7); + u8 int_state = 0; + + if (m_obr & OBR_IE) + { + // find highest priority asserted interrupt source + if ((m_csr & CSR_TIMER) || (m_exr & (EXR_ACLO | EXR_ABO | EXR_PER))) + int_state = 7; + else if (m_lint) + int_state = 6; + else if (m_obr & OBR_RI) + int_state = 5; + else if (m_obr & OBR_TI) + int_state = 4; + else if (m_bint) + int_state = 3; + else if (m_obr & OBR_UI) + int_state = 2; + } + + if (int_state != m_int_state) + { + // deassert old interrupt + if (m_int_state) + m_cpu->set_input_line(m_int_state, CLEAR_LINE); + + // assert new interrupt + if (int_state) + m_cpu->set_input_line(int_state, int_state == 3 ? HOLD_LINE : ASSERT_LINE); + + m_int_state = int_state; + } +} + +void vme_enp10_card_device::cpu_ack(address_map &map) +{ + map(0xff'fff3, 0xff'fff3).lr8(NAME([]() { return m68000_base_device::autovector(1); })); + map(0xff'fff5, 0xff'fff5).lr8(NAME([]() { return m68000_base_device::autovector(2); })); + map(0xff'fff7, 0xff'fff7).lr8(NAME([this]() { m_bint = false; return m68000_base_device::autovector(3); })); + map(0xff'fff9, 0xff'fff9).lr8(NAME([]() { return m68000_base_device::autovector(4); })); + map(0xff'fffb, 0xff'fffb).lr8(NAME([]() { return m68000_base_device::autovector(5); })); + map(0xff'fffd, 0xff'fffd).lr8(NAME([]() { return m68000_base_device::autovector(6); })); + map(0xff'ffff, 0xff'ffff).lr8(NAME([]() { return m68000_base_device::autovector(7); })); +} - m_cpu->set_input_line(INPUT_LINE_IRQ2, enable && BIT(m_uir, 7)); - m_cpu->set_input_line(INPUT_LINE_IRQ3, enable && BIT(m_hir, 7)); - m_cpu->set_input_line(INPUT_LINE_IRQ4, enable && BIT(m_tir, 7)); - m_cpu->set_input_line(INPUT_LINE_IRQ5, enable && BIT(m_rir, 7)); +u16 vme_enp10_card_device::vme_a16_r(offs_t offset, u16 mem_mask) +{ + if (!machine().side_effects_disabled()) + { + if (m_cpu->supervisor_mode()) + return device_vme_card_interface::vme_read16(offset, mem_mask); + else + return device_vme_card_interface::vme_read16(offset, mem_mask); + } + else + return 0; +} + +void vme_enp10_card_device::vme_a16_w(offs_t offset, u16 data, u16 mem_mask) +{ + if (m_cpu->supervisor_mode()) + device_vme_card_interface::vme_write16(offset, data, mem_mask); + else + device_vme_card_interface::vme_write16(offset, data, mem_mask); +} + +u16 vme_enp10_card_device::vme_a24_r(offs_t offset, u16 mem_mask) +{ + if (!machine().side_effects_disabled()) + { + switch (m_cpu->get_fc()) + { + case 1: return device_vme_card_interface::vme_read16(offset, mem_mask); + case 2: return device_vme_card_interface::vme_read16(offset, mem_mask); + case 5: return device_vme_card_interface::vme_read16(offset, mem_mask); + case 6: return device_vme_card_interface::vme_read16(offset, mem_mask); + default: + fatalerror("enp10: unknown vme a24 space read (%s)\n", machine().describe_context()); + } + } + else + return 0; +} + +void vme_enp10_card_device::vme_a24_w(offs_t offset, u16 data, u16 mem_mask) +{ + switch (m_cpu->get_fc()) + { + case 1: device_vme_card_interface::vme_write16(offset, data, mem_mask); break; + case 2: device_vme_card_interface::vme_write16(offset, data, mem_mask); break; + case 5: device_vme_card_interface::vme_write16(offset, data, mem_mask); break; + case 6: device_vme_card_interface::vme_write16(offset, data, mem_mask); break; + default: + fatalerror("enp10: unknown vme a24 space write (%s)\n", machine().describe_context()); + } } diff --git a/src/devices/bus/vme/enp10.h b/src/devices/bus/vme/enp10.h index 3bb1e3ad909..0414a1325d5 100644 --- a/src/devices/bus/vme/enp10.h +++ b/src/devices/bus/vme/enp10.h @@ -8,6 +8,7 @@ #include "cpu/m68000/m68000.h" #include "machine/am79c90.h" +#include "machine/timer.h" #include "bus/vme/vme.h" class vme_enp10_card_device @@ -26,14 +27,27 @@ protected: private: void cpu_map(address_map &map) ATTR_COLD; + void cpu_ack(address_map &map) ATTR_COLD; void vme_map(address_map &map) ATTR_COLD; - u8 addr_r(); - void irq_w(u8 data); + // vme bus outgoing interrupt + u8 vect_r(); + void vect_w(u8 data); u8 iack_r(); + // one-bit register handlers + u8 obr_r(offs_t offset); + void obr_w(offs_t offset, u8 data); + + void timer(timer_device &timer, s32 param); void interrupt(); + // vme bus access handlers + u16 vme_a16_r(offs_t offset, u16 mem_mask); + void vme_a16_w(offs_t offset, u16 data, u16 mem_mask); + u16 vme_a24_r(offs_t offset, u16 mem_mask); + void vme_a24_w(offs_t offset, u16 data, u16 mem_mask); + required_device m_cpu; required_device m_net; @@ -42,13 +56,12 @@ private: u8 m_ivr; // interrupt vector register u8 m_csr; // control/status register - u8 m_ier; // interrupt enable register - u8 m_tir; // transmit interrupt register - u8 m_rir; // receive interrupt register - u8 m_uir; // utility interrupt register - u8 m_rer; // ram/rom enable register + u8 m_obr; // one-bit registers u8 m_exr; // exception register - u8 m_hir; // host interrupt register + + bool m_bint; // host to card interrupt + bool m_lint; // lance interrupt + u8 m_int_state; // current interrupt state memory_view m_boot; }; -- cgit v1.2.3