From e72d1130fe3c185db8c57873f01f0fd0dfa18df6 Mon Sep 17 00:00:00 2001 From: AJR Date: Tue, 28 May 2019 19:56:04 -0400 Subject: mc68340: Timer improvements - Fix prescaler calculation - Handle various reset behaviors - TOUT changes recorded in status register - Better handling of COM flag and compare register being zero - Reload not immediately after reaching zero but on next cycle --- src/devices/machine/68340tmu.cpp | 212 +++++++++++++++++++++++---------------- src/devices/machine/68340tmu.h | 3 +- 2 files changed, 127 insertions(+), 88 deletions(-) diff --git a/src/devices/machine/68340tmu.cpp b/src/devices/machine/68340tmu.cpp index 801a9802557..497bb60131c 100644 --- a/src/devices/machine/68340tmu.cpp +++ b/src/devices/machine/68340tmu.cpp @@ -19,6 +19,7 @@ #define LOG_READ (1U << 2) #define LOG_TIMER (1U << 3) #define LOG_INT (1U << 4) +#define LOG_COUNT (1U << 5) //#define VERBOSE (LOG_SETUP|LOG_INT|LOG_TIMER) @@ -29,6 +30,7 @@ #define LOGR(...) LOGMASKED(LOG_READ, __VA_ARGS__) #define LOGTIMER(...) LOGMASKED(LOG_TIMER, __VA_ARGS__) #define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__) +#define LOGCOUNT(...) LOGMASKED(LOG_COUNT, __VA_ARGS__) #ifdef _MSC_VER #define FUNCNAME __func__ @@ -144,8 +146,7 @@ WRITE16_MEMBER( mc68340_timer_module_device::write ) LOGTIMER("- PCLK: Counter uses %s\n", (data & REG_CR_PCLK) ? "prescaler" : "clock"); LOGTIMER("- CPE: Counter is %s\n", (data & REG_CR_CPE) ? "enabled" : "disabled"); LOGTIMER("- CLK: Clock is %s\n", (data & REG_CR_CLK) ? "TIN (external)" : "system clock / 2"); - LOGTIMER("- Prescaler: Divide by %d\n", (data & REG_CR_POT_MASK) ? ( 1 << ((data & REG_CR_POT_MASK) >> 5)) : 256); - LOGTIMER("- Prescaler: Divide by %d\n", (0x101 << ((data & REG_CR_POT_MASK) >> 5) & 0x1fe)); + LOGTIMER("- Prescaler: Divide by %d\n", (0x101 << ((data & REG_CR_POT_MASK) >> 5) & 0x1ff) * 2); LOGTIMER("- MODE: %s\n", std::array {{ "Input Capture/Output Compare", "Square-Wave Generator - not implemented", @@ -162,40 +163,51 @@ WRITE16_MEMBER( mc68340_timer_module_device::write ) /* The timer is enabled when the counter prescaler enable (CPE) and SWRx bits in the CR are set. Once enabled, the counter enable (ON) bit in the SR is set, and the next falling edge of the counter clock causes the counter to be loaded with the value in the preload 1 - register (PREL1). TODO: make sure of the intial load of PREL1 on first falling flank */ - if (m_cr & REG_CR_SWR) + register (PREL1). */ + if ((m_cr & (REG_CR_SWR | REG_CR_CPE)) == (REG_CR_SWR | REG_CR_CPE)) { - m_sr &= ~REG_SR_COM; - if (m_cr & REG_CR_CPE) + if ((m_sr & REG_SR_ON) == 0) { m_sr |= REG_SR_ON; // Starts the counter LOGTIMER("Starts counter %d\n", m_cpu->get_timer_index(this)); if ((m_cr & REG_CR_CLK) == 0) { - LOGTIMER("- Using system clock/2\n"); - m_timer->adjust(m_cpu->cycles_to_attotime( (m_cpu->clock() / 2) / (0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1fe) * 2) ); + attotime period = m_cpu->cycles_to_attotime((0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1ff) * 2); + LOGTIMER("- Using system clock/2: %f Hz\n", period.as_hz()); + m_timer->adjust(period / 2); } else { LOGTIMER("- Using TIN%d\n", m_cpu->get_timer_index(this)); } } - else + } + else + { + if ((m_sr & REG_SR_ON) != 0) { m_sr &= ~REG_SR_ON; // Stops the counter LOGTIMER("Stops counter %d\n", m_cpu->get_timer_index(this)); m_timer->adjust(attotime::never); } - } - else - { // TODO: Detect Disable mode setting line to three state - if ((m_cr & REG_CR_OC_MASK) == REG_CR_OC_ONE) - { - m_tout_out_cb(ASSERT_LINE); - } - else + + if ((m_cr & REG_CR_SWR) == 0) { - m_tout_out_cb(CLEAR_LINE); + m_sr = (m_sr & ~REG_SR_COM) | 0x00ff; + m_cntr = m_cntr_reg = 0x0000; + + if ((m_cr & REG_CR_OC_MASK) == REG_CR_OC_ONE) + { + tout_set(); + } + else if ((m_cr & REG_CR_OC_MASK) != REG_CR_OC_DISABLED) + { + tout_clear(); + } + else + { + // TODO: Disable mode setting line to three state + } } } break; @@ -289,8 +301,8 @@ TIMER_CALLBACK_MEMBER(mc68340_timer_module_device::timer_callback) do_timer_tick(); if ((m_sr & REG_SR_ON) != 0) { - LOGTIMER("Re-arming timer %d using system clock/2 as base: %d Hz\n", m_cpu->get_timer_index(this) + 1, (m_cpu->clock() / 2) / (0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1fe)); - m_timer->adjust(m_cpu->cycles_to_attotime( (m_cpu->clock() / 2) / (0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1fe) * 2)); + //LOGTIMER("Re-arming timer %d using system clock/2 as base\n", m_cpu->get_timer_index(this) + 1); + m_timer->adjust(m_cpu->cycles_to_attotime((0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1ff) * 2) / 2); } } @@ -310,12 +322,20 @@ void mc68340_timer_module_device::device_start() void mc68340_timer_module_device::device_reset() { + m_mcr = REG_MCR_SUPV; + m_ir = 0x000f; module_reset(); } void mc68340_timer_module_device::module_reset() { - // TODO + m_cr = 0x0000; + m_sr = (m_sr & REG_SR_TG) | 0x00ff; + m_cntr = m_cntr_reg = 0x0000; + m_prel1 = m_prel2 = 0xffff; + m_com = 0x0000; + m_timer->adjust(attotime::never); + m_cpu->update_ipl(); } void mc68340_timer_module_device::do_timer_irq() @@ -341,26 +361,40 @@ void mc68340_timer_module_device::do_timer_tick() if (!((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC && (m_sr & REG_SR_TG) != 0)) m_cntr_reg = m_cntr; + return; } - else // Falling flank + + // Counter decrements on falling flank + if (m_cntr == 0) + { + m_cntr = m_prel1; // TODO: Support prel2 for certain modes + LOGCOUNT("Counter reloaded with %04X\n", m_cntr); + } + else { m_cntr--; + LOGCOUNT("Counter decremented to %04X\n", m_cntr); + } - /* TC - Timer Compare Interrupt - 1 = This bit is set when the counter transitions (off a clock/event falling edge) to the - value in the COM. This bit does not affect the programmed IRQ signal if the IE0 - bit in the CR is cleared. - 0 = This bit is cleared by the timer whenever the RESET signal is asserted on the - IMB, regardless of the mode of operation. This bit may also be cleared by writing - a one to it. Writing a zero to this bit does not alter its contents. This bit is not - affected by disabling the timer (SWR = 0).*/ - if (m_cntr == m_com) // Check COM register - { - m_sr |= REG_SR_COM; - } + /* TC - Timer Compare Interrupt + 1 = This bit is set when the counter transitions (off a clock/event falling edge) to the + value in the COM. This bit does not affect the programmed IRQ signal if the IE0 + bit in the CR is cleared. + 0 = This bit is cleared by the timer whenever the RESET signal is asserted on the + IMB, regardless of the mode of operation. This bit may also be cleared by writing + a one to it. Writing a zero to this bit does not alter its contents. This bit is not + affected by disabling the timer (SWR = 0).*/ + if (m_cntr == 0) + { + LOGINT("Timeout reached\n"); + m_sr &= ~REG_SR_COM; + } + else if (m_cntr == m_com) // Check COM register + { + LOGINT("COM value reached\n"); + m_sr |= REG_SR_COM; } - LOGINT("%s reached\n", m_cntr_reg == 0 ? "Timeout" : "COM value"); /* OC1/OC0 - Output Control These bits select the conditions under which TOUTx changes These bits may have a different effect when in the input capture/output compare mode.*/ @@ -378,18 +412,24 @@ void mc68340_timer_module_device::do_timer_tick() TOUTx is immediately set to zero if the timer is disabled (SWR = 0). If the timer is enabled (SWR = 1), timer compare events toggle TOUTx. (Timer compare events occur when the counter reaches the value stored in the COM.)*/ - if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC) // Detect Input Capture/Output Compare mode + if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC) // Detect Input Capture/Output Compare mode { - if ((m_sr & REG_SR_COM) != 0) // timer reached compare value? + if (m_cntr == m_com) // timer reached compare value? { - m_tout_out_cb((m_tout++ & 1) != 0 ? ASSERT_LINE : CLEAR_LINE); + if ((m_sr & REG_SR_OUT) != 0) + tout_clear(); + else + tout_set(); } } else // Any oher mode { - if (m_cntr_reg == 0) // Counter reached timeout? + if (m_cntr == 0) // Counter reached timeout? { - m_tout_out_cb((m_tout++ & 1) != 0 ? ASSERT_LINE : CLEAR_LINE); + if ((m_sr & REG_SR_OUT) != 0) + tout_clear(); + else + tout_set(); } } break; @@ -402,80 +442,78 @@ void mc68340_timer_module_device::do_timer_tick() immediately set to zero if the timer is disabled (SWR = 0). If the timer is enabled (SWR = 1), TOUTx will be set to zero at timeouts and set to one at timer compare events. If the COM is $0000, TOUTx will be set to zero at the timeout/timer compare event.*/ - if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC) // Detect Input Capture/Output Compare mode + if (m_cntr == 0) // timer reached timeout value? { - if ((m_sr & REG_SR_COM) != 0) // timer reached compare value? - { - m_tout_out_cb(ASSERT_LINE); - } - if (m_cntr_reg == 0) // timer reached timeout value? - { - m_tout_out_cb(CLEAR_LINE); - } + tout_clear(); } - else + else if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC && m_cntr == m_com) // timer reached compare value? { - if (m_cntr_reg == 0) // timer reached timeout value? - { - m_tout_out_cb(CLEAR_LINE); - } + tout_set(); } break; - case REG_CR_OC_ONE: - /* One Mode - If the timer is disabled (SWR = 0) when this encoding is programmed, - TOUTx is immediately set to one. If the timer is enabled (SWR = 1), TOUTx will be set - to one at the next timeout. - - In the input capture/output compare mode, TOUTx is - immediately set to one if the timer is disabled (SWR = 0). If the timer is enabled (SWR = - 1), TOUTx will be set to one at timeouts and set to zero at timer compare events. If the - COM is $0000, TOUTx will be set to one at the timeout/timer compare event.*/ - if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC) // Detect Input Capture/Output Compare mode + case REG_CR_OC_ONE: + /* One Mode - If the timer is disabled (SWR = 0) when this encoding is programmed, + TOUTx is immediately set to one. If the timer is enabled (SWR = 1), TOUTx will be set + to one at the next timeout. + + In the input capture/output compare mode, TOUTx is + immediately set to one if the timer is disabled (SWR = 0). If the timer is enabled (SWR = + 1), TOUTx will be set to one at timeouts and set to zero at timer compare events. If the + COM is $0000, TOUTx will be set to one at the timeout/timer compare event.*/ + if (m_cntr == 0) // timer reached timeout value? { - if ((m_sr & REG_SR_COM) != 0) // timer reached compare value? - { - m_tout_out_cb(CLEAR_LINE); - } - if (m_cntr_reg == 0) // timer reached timeout value? - { - m_tout_out_cb(ASSERT_LINE); - } + tout_set(); } - else + else if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC && m_cntr == m_com) // timer reached compare value? { - if (m_cntr_reg == 0) // timer reached timeout value? - { - m_tout_out_cb(ASSERT_LINE); - } + tout_clear(); } break; default: LOGTIMER("Wrong TOUT mode, fix the code!\n"); } - if (m_cntr_reg == 0) // timer reached timeout value? + if (m_cntr == m_com) // timer reached compare value? ) { - m_sr &= ~REG_SR_COM; - m_cntr = m_prel1; // TODO: Support prel2 for certain modes - m_sr |= REG_SR_TO; - if (m_cr & REG_CR_IE2) + m_sr |= REG_SR_TC; + if (m_cr & REG_CR_IE0) { - LOGTIMER(" - TO interrupt\n"); + LOGTIMER(" - TC interrupt\n"); do_timer_irq(); } } - if ((m_sr & REG_SR_COM) != 0) // timer reached compare value? ) + if (m_cntr == 0) // timer reached timeout value? { - m_sr |= REG_SR_TC; - if (m_cr & REG_CR_IE0) + m_sr |= REG_SR_TO; + if (m_cr & REG_CR_IE2) { - LOGTIMER(" - TC interrupt\n"); + LOGTIMER(" - TO interrupt\n"); do_timer_irq(); } } } } +void mc68340_timer_module_device::tout_set() +{ + if ((m_sr & REG_SR_OUT) == 0) + { + m_sr |= REG_SR_OUT; + LOGTIMER(" - TOUT set\n"); + m_tout_out_cb(1); + } +} + +void mc68340_timer_module_device::tout_clear() +{ + if ((m_sr & REG_SR_OUT) != 0) + { + m_sr &= ~REG_SR_OUT; + LOGTIMER(" - TOUT cleared\n"); + m_tout_out_cb(0); + } +} + mc68340_timer_module_device::mc68340_timer_module_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, MC68340_TIMER_MODULE, tag, owner, clock) , m_tout_out_cb(*this) diff --git a/src/devices/machine/68340tmu.h b/src/devices/machine/68340tmu.h index 8952632792b..0f324337405 100644 --- a/src/devices/machine/68340tmu.h +++ b/src/devices/machine/68340tmu.h @@ -44,7 +44,6 @@ protected: uint16_t m_timer_counter; uint32_t m_tin; uint32_t m_tgate; - uint32_t m_tout; emu_timer *m_timer; devcb_write_line m_tout_out_cb; @@ -52,6 +51,8 @@ protected: devcb_write_line m_tgate_in_cb; void do_timer_irq(); void do_timer_tick(); + void tout_set(); + void tout_clear(); TIMER_CALLBACK_MEMBER(timer_callback); -- cgit v1.2.3