📄 hplatm128timer0asyncp.nc
字号:
* This code is pretty tricky. */
async command void Timer0.set(uint8_t newVal) {
uint8_t curVal = call Timer0.get();
dbg("HplAtm128Timer0AsyncP", "HplAtm128Timer0AsyncP: Setting timer: %hhu\n", newVal);
if (newVal == curVal) {
return;
}
else {
sim_time_t adjustment = curVal - newVal;
adjustment = adjustment << shiftFromScale();
adjustment = clock_to_sim(adjustment);
if (newVal < curVal) {
lastZero += adjustment;
}
else { // newVal > curVal
lastZero -= adjustment;
}
schedule_new_overflow();
notify_changed();
}
}
//=== Read the current timer scale. ===================================
async command uint8_t Timer0.getScale() {
return TCCR0 & 0x7;
}
//=== Turn off the timers. ============================================
async command void Timer0.off() {
call Timer0.setScale(AVR_CLOCK_OFF);
savedCounter = call Timer0.get();
cancel_overflow();
notify_changed();
}
//=== Write a new timer scale. ========================================
async command void Timer0.setScale(uint8_t s) {
Atm128TimerControl_t ctrl;
uint8_t currentScale = call Timer0.getScale();
uint8_t currentCounter;
dbg("HplAtm128Timer0AsyncP", "Timer0 scale set to %i\n", (int)s);
if (currentScale == 0) {
currentCounter = savedCounter;
}
else {
currentCounter = call Timer0.get();
}
ctrl = call Timer0Ctrl.getControl();
ctrl.flat &= ~(0x7);
ctrl.flat |= (s & 0x7);
call Timer0Ctrl.setControl(ctrl);
if (currentScale != s) {
adjust_zero(currentCounter);
schedule_new_overflow();
}
notify_changed();
}
//=== Read the control registers. =====================================
async command Atm128TimerControl_t Timer0Ctrl.getControl() {
return *(Atm128TimerControl_t*)&TCCR0;
}
//=== Write the control registers. ====================================
async command void Timer0Ctrl.setControl( Atm128TimerControl_t x ) {
dbg("HplAtm128Timer0AsyncP", "Setting control to be 0x%hhx\n", x.flat);
TCCR0 = x.flat;
}
//=== Read the interrupt mask. =====================================
async command Atm128_TIMSK_t Timer0Ctrl.getInterruptMask() {
return *(Atm128_TIMSK_t*)&TIMSK;
}
//=== Write the interrupt mask. ====================================
DEFINE_UNION_CAST(TimerMask8_2int, Atm128_TIMSK_t, uint8_t);
DEFINE_UNION_CAST(TimerMask16_2int, Atm128_ETIMSK_t, uint8_t);
async command void Timer0Ctrl.setInterruptMask( Atm128_TIMSK_t x ) {
TIMSK = TimerMask8_2int(x);
}
//=== Read the interrupt flags. =====================================
async command Atm128_TIFR_t Timer0Ctrl.getInterruptFlag() {
Atm128_TIFR_t at;
at.flat = TIFR;
return at;
}
//=== Write the interrupt flags. ====================================
DEFINE_UNION_CAST(TimerFlags8_2int, Atm128_TIFR_t, uint8_t);
DEFINE_UNION_CAST(TimerFlags16_2int, Atm128_ETIFR_t, uint8_t);
async command void Timer0Ctrl.setInterruptFlag( Atm128_TIFR_t x ) {
TIFR = TimerFlags8_2int(x);
}
//=== Timer 8-bit implementation. ====================================
async command void Timer0.reset() {
// Clear TOV0. On real hardware, this is a write.
TIFR &= ~(1 << TOV0);
}
async command void Timer0.start() {
SET_BIT(ATM128_TIMSK, TOIE0);
dbg("HplAtm128Timer0AsyncP", "Enabling TOIE0 at %llu\n", sim_time());
schedule_new_overflow();
}
async command void Timer0.stop() {
dbg("HplAtm128Timer0AsyncP", "Timer stopped @ %llu\n", sim_time());
CLR_BIT(ATM128_TIMSK, TOIE0);
cancel_overflow();
}
bool overflowed() {
return READ_BIT(ATM128_TIFR, TOV0);
}
inline void stabiliseOverflow() {
/* From the atmel manual:
During asynchronous operation, the synchronization of the interrupt
flags for the asynchronous timer takes three processor cycles plus one
timer cycle. The timer is therefore advanced by at least one before
the processor can read the timer value causing the setting of the
interrupt flag. The output compare pin is changed on the timer clock
and is not synchronized to the processor clock.
So: if the timer is = 0, wait till it's = 1, except if
- we're currently in the overflow interrupt handler
- or, the overflow flag is already set
*/
//if (!inOverflow)
// while (!TCNT0 && !overflowed())
//;
}
async command bool Timer0.test() {
stabiliseOverflow();
return overflowed();
}
async command bool Timer0.isOn() {
return (call Timer0Ctrl.getInterruptMask()).bits.toie0;
}
async command void Compare.reset() { TIFR = 1 << OCF0; }
async command void Compare.start() { SET_BIT(ATM128_TIMSK,OCIE0); }
async command void Compare.stop() { CLR_BIT(ATM128_TIMSK,OCIE0); }
async command bool Compare.test() {
return (call Timer0Ctrl.getInterruptFlag()).bits.ocf0;
}
async command bool Compare.isOn() {
return (call Timer0Ctrl.getInterruptMask()).bits.ocie0;
}
//=== Read the compare registers. =====================================
async command uint8_t Compare.get() {
dbg("HplAtm128Timer0AsyncP", "HplAtm128Timer0AsyncP: Getting compare: %hhu\n", OCR0);
return OCR0;
}
//=== Write the compare registers. ====================================
async command void Compare.set(uint8_t t) {
dbg("HplAtm128Timer0AsyncP", "HplAtm128Timer0AsyncP: Setting compare: %hhu\n", t);
atomic {
/* Re the comment above: it's a bad idea to wake up at time 0, as
we'll just spin when setting the next deadline. Try and reduce
the likelihood by delaying the interrupt...
*/
if (t == 0 || t >= 0xfe)
t = 1;
if (t != OCR0) {
OCR0 = t;
schedule_new_compare();
}
}
}
sim_event_t* overflow;
void timer0_overflow_handle(sim_event_t* evt) {
if (evt->cancelled) {
return;
}
else {
if (READ_BIT(ATM128_TIMSK, TOIE0)) {
CLR_BIT(ATM128_TIFR, TOV0);
dbg("HplAtm128Timer0AsyncP", "Overflow interrupt at %s\n", sim_time_string());
SIG_OVERFLOW0();
}
else {
dbg("HplAtm128Timer0AsyncP", "Setting overflow bit at %s\n", sim_time_string());
SET_BIT(ATM128_TIFR, TOV0);
}
configure_overflow(evt);
sim_queue_insert(evt);
}
}
sim_event_t* allocate_overflow() {
sim_event_t* newEvent = sim_queue_allocate_event();
newEvent->handle = timer0_overflow_handle;
newEvent->cleanup = sim_queue_cleanup_none;
return newEvent;
}
void configure_overflow(sim_event_t* evt) {
sim_time_t overflowTime = 0;
uint8_t timerVal = call Timer0.get();
uint8_t overflowVal = 0;
// Calculate how many counter increments until timer
// hits compare, considering wraparound, and special
// case of complete wraparound.
overflowTime = ((overflowVal - timerVal) & 0xff);
if (overflowTime == 0) {
overflowTime = 256;
}
// Now convert the compare time from counter increments
// to simulation ticks, considering the fact that the
// increment actually has a phase offset.
overflowTime = overflowTime << shiftFromScale();
overflowTime = clock_to_sim(overflowTime);
overflowTime += sim_time();
overflowTime -= (sim_time() - last_zero()) % (1 << shiftFromScale());
dbg("HplAtm128Timer0AsyncP", "Scheduling new overflow for %i at time %llu\n", sim_node(), overflowTime);
evt->time = overflowTime;
}
void schedule_new_overflow() {
sim_event_t* newEvent = allocate_overflow();
configure_overflow(newEvent);
if (overflow != NULL) {
cancel_overflow();
}
overflow = newEvent;
sim_queue_insert(newEvent);
}
void cancel_overflow() {
if (overflow != NULL) {
overflow->cancelled = 1;
dbg("HplAtm128Timer0AsyncP", "Cancelling overflow %p.\n", overflow);
overflow->cleanup = sim_queue_cleanup_total;
}
}
async command Atm128Assr_t TimerAsync.getAssr() {
return *(Atm128Assr_t *)&ASSR;
}
async command void TimerAsync.setAssr(Atm128Assr_t x) {
ASSR = x.flat;
}
async command void TimerAsync.setTimer0Asynchronous() {
ASSR |= 1 << AS0;
}
async command int TimerAsync.controlBusy() {
return (ASSR & (1 << TCR0UB)) != 0;
}
async command int TimerAsync.compareBusy() {
return (ASSR & (1 << OCR0UB)) != 0;
}
async command int TimerAsync.countBusy() {
return (ASSR & (1 << TCN0UB)) != 0;
}
void cancel_compare() {
dbg("HplAtm128CompareC", "Cancelling compare at 0x%p\n", compare);
if (compare != NULL) {
compare->cancelled = 1;
compare->cleanup = sim_queue_cleanup_total;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -