// license:GPL-2.0+ // copyright-holders:Couriersud /* * nld_roms.cpp * */ #include "nl_base.h" #include "nl_factory.h" template constexpr bool TOR(N n, T &a) { return (n == 0 ? false : TOR(n-1, a) || a[n-1]()); } template constexpr bool TOR(T &a) { return TOR(a.size(), a); } namespace netlist { namespace devices { NETLIB_OBJECT(mk28000_prom) { NETLIB_CONSTRUCTOR(mk28000_prom) , m_enable_lo(*this, "m_enable_lo", false) , m_enable_hi(*this, "m_enable_hi", false) , m_latched_rom(*this, "m_latched_rom", 0) , m_A(*this, 1, "A{}", NETLIB_DELEGATE(addr)) , m_ARQ(*this, "ARQ", NETLIB_DELEGATE(addr)) , m_OE1(*this, "OE1", NETLIB_DELEGATE(oe1)) , m_OE2(*this, "OE2", NETLIB_DELEGATE(oe2)) , m_O(*this, 1, "O{}", 0) , m_ROM(*this, "ROM") , m_power_pins(*this) { } private: NETLIB_HANDLERI(oe1) { m_enable_lo = m_OE1(); uint8_t o = m_enable_lo ? m_latched_rom : 0; for (std::size_t i=0; i<4; i++) { m_O.set_tristate(!m_enable_lo, NLTIME_FROM_NS(600), NLTIME_FROM_NS(600)); m_O[i].push((o >> i) & 1, NLTIME_FROM_NS(600)); } } NETLIB_HANDLERI(oe2) { m_enable_hi = m_OE2(); uint8_t o = m_enable_hi ? m_latched_rom : 0; for (std::size_t i=4; i<8; i++) { m_O.set_tristate(!m_enable_hi, NLTIME_FROM_NS(600), NLTIME_FROM_NS(600)); m_O[i].push((o >> i) & 1, NLTIME_FROM_NS(600)); } } NETLIB_HANDLERI(addr) { if (!m_ARQ()) { const auto addr = m_A(); m_latched_rom = m_ROM[addr]; } uint8_t o = (m_enable_hi || m_enable_lo) ? m_latched_rom : 0; for (std::size_t i=0; i<4; i++) { m_O.set_tristate(!m_enable_lo, NLTIME_FROM_NS(600), NLTIME_FROM_NS(600)); m_O[i].push((o >> i) & 1, NLTIME_FROM_NS(600)); } for (std::size_t i=4; i<8; i++) { m_O.set_tristate(!m_enable_hi, NLTIME_FROM_NS(600), NLTIME_FROM_NS(600)); m_O[i].push((o >> i) & 1, NLTIME_FROM_NS(600)); } } state_var m_enable_lo; state_var m_enable_hi; state_var m_latched_rom; object_array_t m_A; logic_input_t m_ARQ; logic_input_t m_OE1; logic_input_t m_OE2; object_array_t m_O; param_rom_t m_ROM; nld_power_pins m_power_pins; }; NETLIB_OBJECT(mcm14524_rom) { NETLIB_CONSTRUCTOR_MODEL(mcm14524_rom, "CD4XXX") , m_enabled(*this, "m_enabled", true) , m_latched_rom(*this, "m_latched_rom", 0) , m_A(*this, 1, "A{}", NETLIB_DELEGATE(addr)) , m_CLK(*this, "CLK", NETLIB_DELEGATE(addr)) , m_clk_old(*this, "m_clk_old", true) , m_EN(*this, "EN", NETLIB_DELEGATE(en)) , m_B(*this, 1, "B{}", 0) , m_ROM(*this, "ROM") , m_taccc(*this, "m_taccc", netlist_time::from_nsec(1350)) , m_taccen(*this, "m_taccen", netlist_time::from_nsec(245)) , m_power_pins(*this, NETLIB_DELEGATE(vdd_vss)) { } private: NETLIB_HANDLERI(en) { m_enabled = m_EN(); uint8_t o = m_enabled ? m_latched_rom : 0; // outputs are forced to 0 by enable going low; this chip does not have tri-state outputs! for (std::size_t i=0; i<4; i++) { m_B[i].push((o >> i) & 1, m_taccen); } } NETLIB_HANDLERI(addr) { if (!m_CLK() && m_clk_old) // latch on falling edge { const auto addr = m_A(); m_latched_rom = m_ROM[addr]; } m_clk_old = m_CLK(); uint8_t o = m_enabled ? m_latched_rom : 0; // outputs are forced to 0 by enable going low; this chip does not have tri-state outputs! for (std::size_t i=0; i<4; i++) { m_B[i].push((o >> i) & 1, m_taccc); } } NETLIB_HANDLERI(vdd_vss) { auto d = m_power_pins.VCC()() - m_power_pins.GND()(); if (d > 0.1) // avoid unrealistic values { m_taccc = netlist_time::from_nsec(gsl::narrow_cast(7615.5 / d - 181)); m_taccen = netlist_time::from_nsec(gsl::narrow_cast(1292.5 / d - 14.6)); } } state_var m_enabled; state_var m_latched_rom; object_array_t m_A; logic_input_t m_CLK; state_var m_clk_old; logic_input_t m_EN; object_array_t m_B; param_rom_t m_ROM; state_var m_taccc; // propagation time for data vs CLK state_var m_taccen; // propagation time for data vs /EN nld_power_pins m_power_pins; }; template NETLIB_OBJECT(generic_prom) { NETLIB_CONSTRUCTOR(generic_prom) , m_enabled(*this, "m_enabled", true) , m_TE(*this, "FORCE_TRISTATE_LOGIC", 0) , m_A(*this, 0, "A{}", NETLIB_DELEGATE(addr)) , m_CEQ(*this, 1, D::chip_enable_mask::value ^ static_cast(0xffff), pstring("CE{}"), std::array{ NETLIB_DELEGATE(ce<0>), NETLIB_DELEGATE(ce<1>), NETLIB_DELEGATE(ce<2>)}) , m_O(*this, D::data_name_offset::value, "O{}", m_TE()) , m_ROM(*this, "ROM") , m_power_pins(*this) { } using data_type = typename plib::least_type_for_bits::type; private: template NETLIB_HANDLERI(ce) { using cet = typename D::chip_enable_time; m_enabled = (m_CEQ() == D::chip_enable_mask::value); switch (D::output_id::value) { case 0: // logic { m_O.push(m_ROM[m_A()], D::access_time::value()); } break; case 1: // tristate { m_O.set_tristate(!m_enabled, cet::value(N), cet::value(N)); m_O.push(m_ROM[m_A()], D::access_time::value()); } break; default: // 2, open collector { const auto delay = m_enabled ? D::access_time::value() : cet::value(N); const data_type o = m_enabled ? m_ROM[m_A()] : (1 << D::data_width::value) - 1; m_O.push(o, delay); } break; } } NETLIB_HANDLERI(addr) { if (m_enabled) { m_O.push(m_ROM[m_A()], D::access_time::value()); } } state_var m_enabled; param_logic_t m_TE; object_array_t m_A; object_array_t m_CEQ; object_array_t m_O; param_rom_t m_ROM; nld_power_pins m_power_pins; }; struct desc_82S126 : public desc_base { using address_width = desc_const<8>; using data_width = desc_const<4>; using data_name_offset = desc_const<1>; // O1, O2, .. using chip_enable_inputs = desc_const<2>; // MATCH_MASK : all 0 ==> all bits inverted using chip_enable_mask = desc_const<0x00>; using chip_enable_time = times_ns2<25, 25>; using access_time = time_ns<40>; using output_type = tristate_output_t; using output_id = desc_const<1>; // 0: logic, 1: tristate, 2: open collector }; struct desc_74S287 : public desc_82S126 { using data_name_offset = desc_const<0>; // O0, O1, ... according to National Semiconductor datasheet using chip_enable_time = times_ns2<15, 15>; using access_time = time_ns<35>; }; struct desc_82S123 : public desc_base { // FIXME: tristate outputs, add 82S23 (open collector) using address_width = desc_const<5>; using data_width = desc_const<8>; using data_name_offset = desc_const<0>; // O0, O1, .. using chip_enable_inputs = desc_const<1>; // MATCH_MASK : all 0 ==> all bits inverted using chip_enable_mask = desc_const<0x00>; using chip_enable_time = times_ns1<35>; using access_time = time_ns<45>; using output_type = tristate_output_t; using output_id = desc_const<1>; // 0: logic, 1: tristate, 2: open collector }; struct desc_2716 : public desc_base { // FIXME: tristate outputs using address_width = desc_const<11>; using data_width = desc_const<8>; using data_name_offset = desc_const<0>; // O0, O1, .. using chip_enable_inputs = desc_const<2>; // MATCH_MASK : all 0 ==> all bits inverted using chip_enable_mask = desc_const<0x00>; using chip_enable_time = times_ns2<450, 100>; //CE, OE using access_time = time_ns<450>; using output_type = tristate_output_t; using output_id = desc_const<1>; // 0: logic, 1: tristate, 2: open collector }; using NETLIB_NAME(82S123) = NETLIB_NAME(generic_prom); // 256 bits, 32x8, used as 256x4 using NETLIB_NAME(82S126) = NETLIB_NAME(generic_prom); // 1024 bits, 32x32, used as 256x4 using NETLIB_NAME(74S287) = NETLIB_NAME(generic_prom); // 1024 bits, 32x32, used as 256x4 using NETLIB_NAME(2716) = NETLIB_NAME(generic_prom); // CE2Q = OE, CE1Q = CE using NETLIB_NAME(MK28000) = NETLIB_NAME(mk28000_prom); // 16384 bits, either 2048x8 or 4096x4, determined by OE1/OE2 use using NETLIB_NAME(MCM14524) = NETLIB_NAME(mcm14524_rom); // 1024 bits, 256x4, latched address NETLIB_DEVICE_IMPL(82S126, "PROM_82S126", "+CE1Q,+CE2Q,+A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,@VCC,@GND") NETLIB_DEVICE_IMPL(74S287, "PROM_74S287", "+CE1Q,+CE2Q,+A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,@VCC,@GND") NETLIB_DEVICE_IMPL(82S123, "PROM_82S123", "+CEQ,+A0,+A1,+A2,+A3,+A4,@VCC,@GND") NETLIB_DEVICE_IMPL(2716, "EPROM_2716", "+CE2Q,+CE1Q,+A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,@VCC,@GND") NETLIB_DEVICE_IMPL(MK28000, "PROM_MK28000", "+OE1,+OE2,+ARQ,+A1,+A2,+A3,+A4,+A5,+A6,+A7,+A8,+A9,+A10,+A11,@VCC,@GND") NETLIB_DEVICE_IMPL(MCM14524, "ROM_MCM14524", "+EN,+CLK,+A0,+A1,+A2,+A3,+A4,+A5,+A6,+A7,@VCC,@GND") } //namespace devices } // namespace netlist