summaryrefslogtreecommitdiff
path: root/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'timer.c')
-rw-r--r--timer.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/timer.c b/timer.c
new file mode 100644
index 0000000..b468f9b
--- /dev/null
+++ b/timer.c
@@ -0,0 +1,215 @@
+/*
+ Nios-sim - one simple NIOSII simulator only for personal interest and fun.
+ Copyright (C) 2010 chysun2000@gmail.com
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "public.h"
+#include "timer.h"
+#include "niosii.h"
+
+
+static struct timer_hw hw;
+static uint32_t valid_mask[TIMER_REG_CNT] = {
+ 0x3,0xF,0xFFFF,0xFFFF,0xFFFF,0xFFFF
+};
+
+static uint32_t only_read_mask[TIMER_REG_CNT] = {
+ 0x2,0,0,0,0,0
+};
+
+static void update_period_reg(void);
+static void timer_init(struct io_device * self)
+{
+ int32_t i = 0;
+
+ self->priv_data = &hw;
+
+ for (i=0;i<TIMER_REG_CNT;i++){
+ hw.io_regs[i].addr = TIMER_BASE_ADDR + 4 * i;
+ hw.io_regs[i].value = 0;
+ hw.io_regs[i].valid_mask = valid_mask[i];
+ hw.io_regs[i].only_read_mask = only_read_mask[i];
+ }
+
+ hw.set_period = 0;
+ hw.curr_count = 0;
+
+ printf("Timer Core at 0x%08X-0x%08X\n", hw.io_regs[0].addr,
+ hw.io_regs[TIMER_REG_CNT-1].addr);
+}
+
+static int timer_is_belong(uint32_t address)
+{
+ int32_t ret_val = ADDR_IS_NOT_DEV;
+ if ((address >= TIMER_BASE_ADDR) && (address < (TIMER_BASE_ADDR + TIMER_REG_CNT * 4))){
+ ret_val = ADDR_IS_DEV;
+ }
+
+ return ret_val;
+}
+static void copy_snapshot(void);
+static uint32_t timer_read(struct io_device * self, uint32_t addr, uint32_t data_len)
+{
+ uint32_t ret_val = 0;
+ uint32_t index = 0;
+
+ index = (addr - TIMER_BASE_ADDR) / 4;
+ if (index >= 0 && index < TIMER_REG_CNT){
+ if (index == TIM_REG_SNAPL || index == TIM_REG_SNAPH){
+ copy_snapshot();
+ }
+ ret_val = hw.io_regs[index].value & hw.io_regs[index].valid_mask;
+ ret_val = io_read_data(ret_val, data_len);
+ }
+ return ret_val;
+}
+
+static void timer_write(struct io_device * self, uint32_t addr, uint32_t data, uint32_t data_len)
+{
+ uint32_t index = 0;
+ uint32_t temp = 0;
+ uint32_t only_read_mask = 0;
+ uint32_t valid_mask = 0;
+
+ index = (addr - TIMER_BASE_ADDR) / 4;
+
+ if (index >= 0 && index < TIMER_REG_CNT){
+ temp = hw.io_regs[index].value;
+ valid_mask = hw.io_regs[index].valid_mask;
+ only_read_mask = hw.io_regs[index].only_read_mask;
+ hw.io_regs[index].value = io_write_data_mask(temp, data, data_len, valid_mask, only_read_mask);
+
+
+ if (index == TIM_REG_PERIODL){
+ hw.set_period = hw.set_period &0xFFFF0000;
+ hw.set_period = hw.set_period | (data & 0xFFFF);
+ hw.curr_count = hw.set_period;
+ update_period_reg();
+ }
+ else if (index == TIM_REG_PERIODH){
+ hw.set_period = hw.set_period & 0xFFFF;
+ hw.set_period = hw.set_period | ((data & 0xFFFF) <<16);
+ hw.curr_count = hw.set_period;
+ update_period_reg();
+ }
+ else if (index == TIM_REG_STATUS){
+ if ((hw.io_regs[index].value & STATUS_TO_MASK) == 0){
+ clean_ipending(self->irq_enable_mask);
+ }
+ }
+ }
+}
+
+static int32_t timer_has_irq(struct io_device * self)
+{
+ int32_t ret_val = DEV_NO_IRQ;
+ uint32_t ctrl_reg_val = 0;
+ uint32_t status_reg_val = 0;
+
+ ctrl_reg_val = hw.io_regs[TIM_REG_CTRL].value & hw.io_regs[TIM_REG_CTRL].valid_mask;
+ status_reg_val = hw.io_regs[TIM_REG_STATUS].value & hw.io_regs[TIM_REG_STATUS].valid_mask;
+
+ if ((ctrl_reg_val & CTRL_ITO_MASK) == CTRL_ITO_MASK){
+ if ((status_reg_val & STATUS_TO_MASK) == STATUS_TO_MASK){
+ ret_val = DEV_HAS_IRQ;
+ }
+ }
+ return ret_val;
+}
+
+static void copy_snapshot(void)
+{
+ hw.io_regs[TIM_REG_SNAPL].value = hw.io_regs[TIM_REG_PERIODL].value;
+ hw.io_regs[TIM_REG_SNAPH].value = hw.io_regs[TIM_REG_PERIODH].value;
+}
+
+static void update_period_reg(void)
+{
+ hw.io_regs[TIM_REG_PERIODL].value = hw.curr_count & 0xFFFF;
+ hw.io_regs[TIM_REG_PERIODH].value = (hw.curr_count & 0xFFFF0000) >> 16;
+ //copy_snapshot();
+}
+
+static void decrease_counter(void)
+{
+ if (hw.curr_count > 2){
+ hw.curr_count -= 2;
+ }
+ else {
+ hw.curr_count = 0;
+ }
+ update_period_reg();
+}
+
+static int32_t timer_can_decrease(void)
+{
+ int32_t ret = SIM_TRUE;
+ uint32_t reg_ctrl_val = hw.io_regs[TIM_REG_CTRL].value;
+
+ if ((reg_ctrl_val & CTRL_START_MASK) == 0){
+ ret = SIM_FALSE;
+ goto out;
+ }
+
+ if ((reg_ctrl_val & CTRL_STOP_MASK) != 0){
+ ret = SIM_FALSE;
+ goto out;
+ }
+
+ if (hw.set_period == hw.curr_count){
+ if ((reg_ctrl_val & CTRL_CONT_MASK) == 0){
+ ret = SIM_FALSE;
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+static void update_status(void)
+{
+ if(hw.curr_count <= 0){
+ hw.io_regs[TIM_REG_CTRL].value |= CTRL_ITO_MASK;
+ hw.curr_count = hw.set_period;
+ update_period_reg();
+ hw.io_regs[TIM_REG_STATUS].value |= STATUS_TO_MASK;
+ }
+}
+
+static void timer_simulate(struct io_device * self)
+{
+ if (timer_can_decrease() == SIM_TRUE) {
+ decrease_counter();
+ update_status();
+ }
+}
+
+struct io_device timer_core = {
+ .name = "timer_core",
+ .init = timer_init,
+ .is_belong = timer_is_belong,
+ .read_data = timer_read,
+ .write_data = timer_write,
+ .has_irq = timer_has_irq,
+ .simulate = timer_simulate,
+ .irq_enable_mask = TIM_IRQ_MASK,
+};
+
+