From: Michel Pollet Date: Sun, 20 Dec 2009 23:04:43 +0000 (+0000) Subject: timer: Masssive timer update. 8 & 16 bits X-Git-Tag: v1.0a1~40 X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=501725d8c3dca6d908eb5c5c5e7d5831672fdb37;p=sx%2Fsimavr.git timer: Masssive timer update. 8 & 16 bits Re-massaged the timer code. It now works as 8 or 16 bits, Also added a way to soecify the mode the timer run, and made the TOV, COMPA and COMPB work as they should. Now support the "Normal" timer mode too. Signed-off-by: Michel Pollet --- diff --git a/simavr/sim/avr_timer.c b/simavr/sim/avr_timer.c new file mode 100644 index 0000000..fc3d3c7 --- /dev/null +++ b/simavr/sim/avr_timer.c @@ -0,0 +1,198 @@ +/* + avr_timer.c + + Handles the 8 bits and 16 bits AVR timer. + Handles + + CDC + + Fast PWM + + Copyright 2008, 2009 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include +#include "avr_timer.h" + +static avr_cycle_count_t avr_timer_compa(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_timer_t * p = (avr_timer_t *)param; + avr_raise_interrupt(avr, &p->compa); + return p->tov_cycles ? 0 : p->compa_cycles ? when + p->compa_cycles : 0; +} + +static avr_cycle_count_t avr_timer_compb(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_timer_t * p = (avr_timer_t *)param; + avr_raise_interrupt(avr, &p->compb); + return p->tov_cycles ? 0 : p->compb_cycles ? when + p->compb_cycles : 0; +} + +static avr_cycle_count_t avr_timer_tov(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_timer_t * p = (avr_timer_t *)param; + avr_raise_interrupt(avr, &p->overflow); + + if (p->compa_cycles && p->tov_cycles > p->compa_cycles) + avr_cycle_timer_register(avr, p->compa_cycles, avr_timer_compa, p); + if (p->compb_cycles && p->tov_cycles > p->compb_cycles) + avr_cycle_timer_register(avr, p->compb_cycles, avr_timer_compb, p); + return when + p->tov_cycles; +} + +static uint8_t avr_timer_tcnt_read(struct avr_t * avr, avr_io_addr_t addr, void * param) +{ + //avr_timer_t * p = (avr_timer_t *)param; + // made to trigger potential watchpoints + return avr_core_watch_read(avr, addr); +} + +/* + * The timers are /always/ 16 bits here, if the higher byte register + * is specified it's just added. + */ +static uint16_t _timer_get_ocra(avr_timer_t * p) +{ + return p->io.avr->data[p->r_ocra] | + (p->r_ocrah ? (p->io.avr->data[p->r_ocrah] << 8) : 0); +} +static uint16_t _timer_get_ocrb(avr_timer_t * p) +{ + return p->io.avr->data[p->r_ocrb] | + (p->r_ocrbh ? (p->io.avr->data[p->r_ocrbh] << 8) : 0); +} + +static void avr_timer_configure(avr_timer_t * p, uint32_t clock, uint32_t top) +{ + uint32_t ocra = _timer_get_ocra(p); + uint32_t ocrb = _timer_get_ocrb(p); + float fa = clock / (float)(ocra+1), fb = clock / (float)(ocrb+1); + float t = clock / (float)(top+1); + float frequency = p->io.avr->frequency; + + p->compa_cycles = p->compb_cycles = p->tov_cycles = 0; + + printf("%s-%c clock %d top %d a %d b %d\n", __FUNCTION__, p->name, clock, top, ocra, ocrb); + if (top != ocra) { + p->tov_cycles = frequency / t; // avr_hz_to_cycles(frequency, t); + printf("%s-%c TOP %.2fHz = cycles = %d\n", __FUNCTION__, p->name, t, (int)p->tov_cycles); + } + if (ocra && ocra <= top) { + p->compa_cycles = frequency / fa; // avr_hz_to_cycles(p->io.avr, fa); + printf("%s-%c A %.2fHz = cycles = %d\n", __FUNCTION__, p->name, fa, (int)p->compa_cycles); + } + if (ocrb && ocrb <= top) { + p->compb_cycles = frequency / fb; // avr_hz_to_cycles(p->io.avr, fb); + printf("%s-%c B %.2fHz = cycles = %d\n", __FUNCTION__, p->name, fb, (int)p->compb_cycles); + } + + if (p->tov_cycles > 1) + avr_cycle_timer_register(p->io.avr, p->tov_cycles, avr_timer_tov, p); + else { + if (p->compa_cycles > 1) + avr_cycle_timer_register(p->io.avr, p->compa_cycles, avr_timer_compa, p); + if (p->compb_cycles > 1) + avr_cycle_timer_register(p->io.avr, p->compb_cycles, avr_timer_compb, p); + } +} + +static void avr_timer_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) +{ + avr_timer_t * p = (avr_timer_t *)param; + + p->compa_cycles = 0; + p->compb_cycles = 0; + + avr_core_watch_write(avr, addr, v); + long clock = avr->frequency; + + // only can exists on "asynchronous" 8 bits timers + if (avr_regbit_get(avr, p->as2)) + clock = 32768; + + uint8_t cs = avr_regbit_get_array(avr, p->cs, ARRAY_SIZE(p->cs)); + if (cs == 0) { + printf("%s-%c clock turned off\n", __FUNCTION__, p->name); + avr_cycle_timer_cancel(avr, avr_timer_tov, p); + avr_cycle_timer_cancel(avr, avr_timer_compa, p); + avr_cycle_timer_cancel(avr, avr_timer_compb, p); + return; + } + + uint8_t mode = avr_regbit_get_array(avr, p->wgm, ARRAY_SIZE(p->wgm)); + uint8_t cs_div = p->cs_div[cs]; + uint32_t f = clock >> cs_div; + + printf("%s-%c clock %d, div %d(/%d) = %d ; mode %d\n", __FUNCTION__, p->name, clock, cs, 1 << cs_div, f, mode); + switch (p->wgm_op[mode].kind) { + case avr_timer_wgm_normal: + avr_timer_configure(p, f, (1 << p->wgm_op[mode].size) - 1); + break; + case avr_timer_wgm_ctc: { + avr_timer_configure(p, f, _timer_get_ocra(p)); + } break; + case avr_timer_wgm_fast_pwm: { + avr_raise_irq(p->io.irq + TIMER_IRQ_OUT_PWM0, _timer_get_ocra(p)); + avr_raise_irq(p->io.irq + TIMER_IRQ_OUT_PWM1, _timer_get_ocra(p)); + } break; + default: + printf("%s-%c unsupported timer mode wgm=%d (%d)\n", __FUNCTION__, p->name, mode, p->wgm_op[mode].kind); + } +} + +static void avr_timer_reset(avr_io_t * port) +{ + avr_timer_t * p = (avr_timer_t *)port; + avr_cycle_timer_cancel(p->io.avr, avr_timer_tov, p); + avr_cycle_timer_cancel(p->io.avr, avr_timer_compa, p); + avr_cycle_timer_cancel(p->io.avr, avr_timer_compb, p); + p->compa_cycles = 0; + p->compb_cycles = 0; +} + +static avr_io_t _io = { + .kind = "timer", + .reset = avr_timer_reset, +}; + +void avr_timer_init(avr_t * avr, avr_timer_t * p) +{ + p->io = _io; + printf("%s timer%c created OCRA %02x OCRAH %02x\n", __FUNCTION__, p->name, p->r_ocra, p->r_ocrah); + + // allocate this module's IRQ + p->io.irq_count = TIMER_IRQ_COUNT; + p->io.irq = avr_alloc_irq(0, p->io.irq_count); + p->io.irq_ioctl_get = AVR_IOCTL_TIMER_GETIRQ(p->name); + p->io.irq[TIMER_IRQ_OUT_PWM0].flags |= IRQ_FLAG_FILTERED; + p->io.irq[TIMER_IRQ_OUT_PWM1].flags |= IRQ_FLAG_FILTERED; + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->compa); + avr_register_vector(avr, &p->compb); + + avr_register_io_write(avr, p->cs[0].reg, avr_timer_write, p); + + /* + * Even if the timer is 16 bits, we don't care to have watches on the + * high bytes because the datasheet says that the low address is always + * the trigger. + */ + avr_register_io_write(avr, p->r_ocra, avr_timer_write, p); + avr_register_io_write(avr, p->r_ocrb, avr_timer_write, p); + + avr_register_io_read(avr, p->r_tcnt, avr_timer_tcnt_read, p); +} diff --git a/simavr/sim/avr_timer.h b/simavr/sim/avr_timer.h new file mode 100644 index 0000000..cd5f8b4 --- /dev/null +++ b/simavr/sim/avr_timer.h @@ -0,0 +1,78 @@ +/* + avr_timer.h + + Copyright 2008, 2009 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#ifndef AVR_TIMER_H_ +#define AVR_TIMER_H_ + +#include "sim_avr.h" + +enum { + TIMER_IRQ_OUT_PWM0 = 0, + TIMER_IRQ_OUT_PWM1, + TIMER_IRQ_COUNT +}; + +// Get the internal IRQ corresponding to the INT +#define AVR_IOCTL_TIMER_GETIRQ(_name) AVR_IOCTL_DEF('t','m','r',(_name)) + +enum { + avr_timer_wgm_none = 0, // invalid mode + avr_timer_wgm_normal, + avr_timer_wgm_ctc, + avr_timer_wgm_fast_pwm, +}; +typedef struct avr_timer_wgm_t { + uint32_t top: 8, bottom: 8, size : 8, kind : 8; +} avr_timer_wgm_t; + +#define AVR_TIMER_WGM_NORMAL8() { .kind = avr_timer_wgm_normal, .size=8 } +#define AVR_TIMER_WGM_NORMAL16() { .kind = avr_timer_wgm_normal, .size=16 } +#define AVR_TIMER_WGM_CTC() { .kind = avr_timer_wgm_ctc } +#define AVR_TIMER_WGM_FASTPWM() { .kind = avr_timer_wgm_fast_pwm } + +typedef struct avr_timer_t { + avr_io_t io; + char name; + avr_regbit_t disabled; // bit in the PRR + + avr_io_addr_t r_ocra, r_ocrb, r_ocrc, r_tcnt, r_icr; + avr_io_addr_t r_ocrah, r_ocrbh, r_tcnth, r_icrh; + + avr_regbit_t wgm[4]; + avr_timer_wgm_t wgm_op[16]; + + avr_regbit_t cs[4]; + uint8_t cs_div[16]; + avr_regbit_t as2; // asynchronous clock 32khz + + avr_int_vector_t compa; // comparator A + avr_int_vector_t compb; // comparator A + avr_int_vector_t overflow; // overflow + avr_int_vector_t icr; // input capture + + uint64_t compa_cycles; + uint64_t compb_cycles; + uint64_t tov_cycles; +} avr_timer_t; + +void avr_timer_init(avr_t * avr, avr_timer_t * port); + +#endif /* AVR_TIMER_H_ */ diff --git a/simavr/sim/avr_timer8.c b/simavr/sim/avr_timer8.c deleted file mode 100644 index fe94592..0000000 --- a/simavr/sim/avr_timer8.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - avr_timer8.c - - Handles the just one mode of the 8 bit AVR timer. - Still need to handle all the others! - - Copyright 2008, 2009 Michel Pollet - - This file is part of simavr. - - simavr is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - simavr is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with simavr. If not, see . - */ - -#include -#include "avr_timer8.h" - -static avr_cycle_count_t avr_timer8_compa(struct avr_t * avr, avr_cycle_count_t when, void * param) -{ - avr_timer8_t * p = (avr_timer8_t *)param; - avr_raise_interrupt(avr, &p->compa); - return p->compa_cycles ? when + p->compa_cycles : 0; -} - -static avr_cycle_count_t avr_timer8_compb(struct avr_t * avr, avr_cycle_count_t when, void * param) -{ - avr_timer8_t * p = (avr_timer8_t *)param; - avr_raise_interrupt(avr, &p->compb); - return p->compb_cycles ? when + p->compb_cycles : 0; -} - -static uint8_t avr_timer8_tcnt_read(struct avr_t * avr, avr_io_addr_t addr, void * param) -{ - //avr_timer8_t * p = (avr_timer8_t *)param; - // made to trigger potential watchpoints - return avr_core_watch_read(avr, addr); -} - -static void avr_timer8_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) -{ - avr_timer8_t * p = (avr_timer8_t *)param; - - p->compa_cycles = 0; - p->compb_cycles = 0; - - avr_core_watch_write(avr, addr, v); - long clock = avr->frequency; - if (avr_regbit_get(avr, p->as2)) - clock = 32768; - uint8_t cs = avr_regbit_get_array(avr, p->cs, ARRAY_SIZE(p->cs)); - if (cs == 0) { - printf("%s-%c clock turned off\n", __FUNCTION__, p->name); - avr_cycle_timer_cancel(avr, avr_timer8_compa, p); - avr_cycle_timer_cancel(avr, avr_timer8_compb, p); - return; - } - uint8_t mode = avr_regbit_get_array(avr, p->wgm, ARRAY_SIZE(p->wgm)); - uint8_t cs_div = p->cs_div[cs]; - uint16_t ocra = avr->data[p->r_ocra]; - uint16_t ocrb = avr->data[p->r_ocrb]; - - if (mode == 0) { - long f = clock >> cs_div; - long fa = f / (ocra+1), fb = f / (ocrb+1); - - // printf("%s-%c clock f=%ld cs=%02x (div %d) = %ldhz\n", __FUNCTION__, p->name, clock, cs, 1 << cs_div, f); - if (ocra) printf("%s-%c wgm %d OCRA=%3d = %ldhz\n", __FUNCTION__, p->name, mode, ocra, fa); - if (ocrb) printf("%s-%c wgm %d OCRB=%3d = %ldhz\n", __FUNCTION__, p->name, mode, ocrb, fb); - - p->compa_cycles = avr_hz_to_cycles(avr, fa); - p->compb_cycles = avr_hz_to_cycles(avr, fb); - if (p->compa_cycles > 1) - avr_cycle_timer_register(avr, p->compa_cycles, avr_timer8_compa, p); - if (p->compb_cycles > 1) - avr_cycle_timer_register(avr, p->compb_cycles, avr_timer8_compb, p); - printf("%s-%c A %ld/%ld = cycles = %d\n", __FUNCTION__, p->name, (long)avr->frequency, fa, (int)p->compa_cycles); - printf("%s-%c B %ld/%ld = cycles = %d\n", __FUNCTION__, p->name, (long)avr->frequency, fb, (int)p->compb_cycles); - } else if (mode == 3) { // fast pwm - avr_raise_irq(p->io.irq + TIMER8_IRQ_OUT_PWM0, ocra); - avr_raise_irq(p->io.irq + TIMER8_IRQ_OUT_PWM1, ocrb); - } -} - -static void avr_timer8_reset(avr_io_t * port) -{ - avr_timer8_t * p = (avr_timer8_t *)port; - avr_cycle_timer_cancel(p->io.avr, avr_timer8_compa, p); - avr_cycle_timer_cancel(p->io.avr, avr_timer8_compb, p); - p->compa_cycles = 0; - p->compb_cycles = 0; -} - -static avr_io_t _io = { - .kind = "timer8", - .reset = avr_timer8_reset, -}; - -void avr_timer8_init(avr_t * avr, avr_timer8_t * p) -{ - p->io = _io; -// printf("%s timer%c created\n", __FUNCTION__, p->name); - - // allocate this module's IRQ - p->io.irq_count = TIMER8_IRQ_COUNT; - p->io.irq = avr_alloc_irq(0, p->io.irq_count); - p->io.irq_ioctl_get = AVR_IOCTL_TIMER8_GETIRQ(p->name); - p->io.irq[TIMER8_IRQ_OUT_PWM0].flags |= IRQ_FLAG_FILTERED; - p->io.irq[TIMER8_IRQ_OUT_PWM1].flags |= IRQ_FLAG_FILTERED; - - avr_register_io(avr, &p->io); - avr_register_vector(avr, &p->compa); - avr_register_vector(avr, &p->compb); - - avr_register_io_write(avr, p->cs[0].reg, avr_timer8_write, p); - avr_register_io_write(avr, p->r_ocra, avr_timer8_write, p); - avr_register_io_write(avr, p->r_ocrb, avr_timer8_write, p); - - avr_register_io_read(avr, p->r_tcnt, avr_timer8_tcnt_read, p); -} diff --git a/simavr/sim/avr_timer8.h b/simavr/sim/avr_timer8.h deleted file mode 100644 index 8f661c2..0000000 --- a/simavr/sim/avr_timer8.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - avr_timer8.h - - Copyright 2008, 2009 Michel Pollet - - This file is part of simavr. - - simavr is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - simavr is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with simavr. If not, see . - */ - -#ifndef AVR_TIMER8_H_ -#define AVR_TIMER8_H_ - -#include "sim_avr.h" - -enum { - TIMER8_IRQ_OUT_PWM0 = 0, - TIMER8_IRQ_OUT_PWM1, - TIMER8_IRQ_COUNT -}; - -// Get the internal IRQ corresponding to the INT -#define AVR_IOCTL_TIMER8_GETIRQ(_name) AVR_IOCTL_DEF('t','i','8',(_name)) - - -typedef struct avr_timer8_t { - avr_io_t io; - char name; - avr_regbit_t disabled; // bit in the PRR - - avr_io_addr_t r_ocra, r_ocrb, r_ocrc, r_tcnt; - - avr_regbit_t wgm[4]; - avr_regbit_t cs[4]; - uint8_t cs_div[16]; - avr_regbit_t as2; // asynchronous clock 32khz - - avr_int_vector_t compa; // comparator A - avr_int_vector_t compb; // comparator A - avr_int_vector_t overflow; // overflow - - uint32_t compa_cycles; - uint32_t compb_cycles; -} avr_timer8_t; - -void avr_timer8_init(avr_t * avr, avr_timer8_t * port); - -#endif /* AVR_TIMER8_H_ */