/* 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 #include #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_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, };