diff options
author | Pavel Pisa <pisa@cmp.felk.cvut.cz> | 2019-03-04 15:30:11 +0100 |
---|---|---|
committer | Pavel Pisa <pisa@cmp.felk.cvut.cz> | 2019-03-04 15:30:11 +0100 |
commit | 1dc09ab41ae703a1f15be87cacab5e842cf1a09d (patch) | |
tree | 392908ad6642e1ad5f9afa8c21998665a83534af /qtmips_machine | |
parent | cd3a4b268a9b3d4d5436fee5de7967adb6f6e170 (diff) | |
download | qtmips-1dc09ab41ae703a1f15be87cacab5e842cf1a09d.tar.gz qtmips-1dc09ab41ae703a1f15be87cacab5e842cf1a09d.tar.bz2 qtmips-1dc09ab41ae703a1f15be87cacab5e842cf1a09d.zip |
Implemented interrupt delivery and processing for serial port.
Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
Diffstat (limited to 'qtmips_machine')
-rw-r--r-- | qtmips_machine/cop0state.cpp | 30 | ||||
-rw-r--r-- | qtmips_machine/cop0state.h | 13 | ||||
-rw-r--r-- | qtmips_machine/core.cpp | 14 | ||||
-rw-r--r-- | qtmips_machine/qtmipsmachine.cpp | 4 | ||||
-rw-r--r-- | qtmips_machine/qtmipsmachine.h | 1 | ||||
-rw-r--r-- | qtmips_machine/serialport.cpp | 41 | ||||
-rw-r--r-- | qtmips_machine/serialport.h | 10 |
7 files changed, 109 insertions, 4 deletions
diff --git a/qtmips_machine/cop0state.cpp b/qtmips_machine/cop0state.cpp index ea937ce..9c5fa1c 100644 --- a/qtmips_machine/cop0state.cpp +++ b/qtmips_machine/cop0state.cpp @@ -91,7 +91,7 @@ const Cop0State::cop0reg_desc_t Cop0State::cop0reg_desc[Cop0State::COP0REGS_CNT] &Cop0State::read_cop0reg_default, &Cop0State::write_cop0reg_default}, [Cop0State::Compare] = {"Compare", 0x00000000, &Cop0State::read_cop0reg_default, &Cop0State::write_cop0reg_default}, - [Cop0State::Status] = {"Status", 0x00000000, + [Cop0State::Status] = {"Status", Status_IE | Status_IntMask, &Cop0State::read_cop0reg_default, &Cop0State::write_cop0reg_default}, [Cop0State::Cause] = {"Cause", 0x00000000, &Cop0State::read_cop0reg_default, &Cop0State::write_cop0reg_default}, @@ -178,3 +178,31 @@ void Cop0State::update_execption_cause(enum ExceptionCause excause, bool in_dela if (excause != EXCAUSE_INT) cop0reg[(int)Cause] |= (int)excause << 2; } + +void Cop0State::set_interrupt_signal(uint irq_num, bool active) { + std::uint32_t mask; + if (irq_num >= 8) + return; + mask = Status_Int0 << irq_num; + if (active) + cop0reg[(int)Cause] |= mask; + else + cop0reg[(int)Cause] &= ~mask; +} + +bool Cop0State::core_interrupt_request() { + std::uint32_t irqs; + irqs = cop0reg[(int)Status]; + irqs &= cop0reg[(int)Cause]; + irqs &= Status_IntMask; + + return !!(irqs && cop0reg[(int)Status] & Status_IntMask && + !(cop0reg[(int)Status] & Status_EXL)); +} + +void Cop0State::set_status_exl(bool value) { + if (value) + cop0reg[(int)Status] |= Status_EXL; + else + cop0reg[(int)Status] &= ~Status_EXL; +} diff --git a/qtmips_machine/cop0state.h b/qtmips_machine/cop0state.h index 880dea2..649348e 100644 --- a/qtmips_machine/cop0state.h +++ b/qtmips_machine/cop0state.h @@ -61,6 +61,13 @@ public: COP0REGS_CNT, }; + enum StatusReg { + Status_IE = 0x00000001, + Status_EXL = 0x00000002, + Status_IntMask = 0x0000ff00, + Status_Int0 = 0x00000100, + }; + Cop0State(Core *core = nullptr); Cop0State(const Cop0State&); @@ -74,6 +81,12 @@ public: void reset(); // Reset all values to zero + bool core_interrupt_request(); + +public slots: + void set_interrupt_signal(uint irq_num, bool active); + void set_status_exl(bool value); + protected: void setup_core(Core *core); void update_execption_cause(enum ExceptionCause excause, bool in_delay_slot); diff --git a/qtmips_machine/core.cpp b/qtmips_machine/core.cpp index b845360..22dfdc8 100644 --- a/qtmips_machine/core.cpp +++ b/qtmips_machine/core.cpp @@ -138,6 +138,12 @@ bool Core::handle_exception(Core *core, Registers *regs, ExceptionCause excause, else cop0state->write_cop0reg(Cop0State::EPC, inst_addr); cop0state->update_execption_cause(excause, in_delay_slot); + if (cop0state->read_cop0reg(Cop0State::EBase) != 0) { + if (excause == EXCAUSE_INT) { + cop0state->set_status_exl(true); + regs->pc_abs_jmp(cop0state->read_cop0reg(Cop0State::EBase)); + } + } } ExceptionHandler *exhandler = ex_handlers.value(excause); @@ -228,6 +234,12 @@ struct Core::dtFetch Core::fetch(bool skip_break) { excause = EXCAUSE_HWBREAK; } } + if (cop0state != nullptr && excause == EXCAUSE_NONE) { + if (cop0state->core_interrupt_request()) { + excause = EXCAUSE_INT; + } + } + emit fetch_inst_addr_value(inst_addr); emit instruction_fetched(inst, inst_addr, excause); return { @@ -399,6 +411,8 @@ struct Core::dtExecute Core::execute(const struct dtDecode &dt) { break; case ALU_OP_ERET: regs->pc_abs_jmp(cop0state->read_cop0reg(Cop0State::EPC)); + if (cop0state != nullptr) + cop0state->set_status_exl(false); break; default: break; diff --git a/qtmips_machine/qtmipsmachine.cpp b/qtmips_machine/qtmipsmachine.cpp index 0ce1662..41ca56f 100644 --- a/qtmips_machine/qtmipsmachine.cpp +++ b/qtmips_machine/qtmipsmachine.cpp @@ -63,6 +63,8 @@ QtMipsMachine::QtMipsMachine(const MachineConfig &cc, bool load_symtab) : ser_port = new SerialPort(); addressapce_insert_range(ser_port, 0xffffc000, 0xffffc03f, true); addressapce_insert_range(ser_port, 0xffff0000, 0xffff003f, false); + connect(ser_port, SIGNAL(signal_interrupt(uint,bool)), + this, SIGNAL(set_interrupt_signal(uint,bool))); perip_spi_led = new PeripSpiLed(); addressapce_insert_range(perip_spi_led, 0xffffc100, 0xffffc1ff, true); @@ -87,6 +89,8 @@ QtMipsMachine::QtMipsMachine(const MachineConfig &cc, bool load_symtab) : else cr = new CoreSingle(regs, cch_program, cch_data, cc.delay_slot(), min_cache_row_size, cop0state); + connect(this, SIGNAL(set_interrupt_signal(uint,bool)), + cop0state, SLOT(set_interrupt_signal(uint,bool))); run_t = new QTimer(this); set_speed(0); // In default run as fast as possible diff --git a/qtmips_machine/qtmipsmachine.h b/qtmips_machine/qtmipsmachine.h index 51b5424..60291a8 100644 --- a/qtmips_machine/qtmipsmachine.h +++ b/qtmips_machine/qtmipsmachine.h @@ -105,6 +105,7 @@ signals: void status_change(enum machine::QtMipsMachine::Status st); void tick(); // Time tick void post_tick(); // Emitted after tick to allow updates + void set_interrupt_signal(uint irq_num, bool active); private slots: void step_timer(); diff --git a/qtmips_machine/serialport.cpp b/qtmips_machine/serialport.cpp index c636d38..7eb9c53 100644 --- a/qtmips_machine/serialport.cpp +++ b/qtmips_machine/serialport.cpp @@ -53,6 +53,10 @@ SerialPort::SerialPort() { rx_st_reg = 0; rx_data_reg = 0; tx_st_reg = 0; + tx_irq_level = 0; + rx_irq_level = 1; + tx_irq_active = false; + rx_irq_active = false; } SerialPort::~SerialPort() { @@ -63,11 +67,12 @@ void SerialPort::pool_rx_byte() const { unsigned int byte = 0; bool available = false; if (!(rx_st_reg & SERP_RX_ST_REG_READY_m)) { + rx_st_reg |= SERP_RX_ST_REG_READY_m; emit rx_byte_pool(0, byte, available); - if (available) { + if (available) rx_data_reg = byte; - rx_st_reg |= SERP_RX_ST_REG_READY_m; - } + else + rx_st_reg &= ~SERP_RX_ST_REG_READY_m; } } @@ -82,13 +87,17 @@ bool SerialPort::wword(std::uint32_t address, std::uint32_t value) { case SERP_RX_ST_REG_o: rx_st_reg &= ~SERP_RX_ST_REG_IE_m; rx_st_reg |= value & SERP_RX_ST_REG_IE_m; + rx_queue_check(); + update_rx_irq(); break; case SERP_TX_ST_REG_o: tx_st_reg &= ~SERP_TX_ST_REG_IE_m; tx_st_reg |= value & SERP_TX_ST_REG_IE_m; + update_tx_irq(); break; case SERP_TX_DATA_REG_o: emit tx_byte(value & 0xff); + update_tx_irq(); break; } return true; @@ -111,9 +120,11 @@ std::uint32_t SerialPort::rword(std::uint32_t address, bool debug_access) const if (rx_st_reg & SERP_RX_ST_REG_READY_m) { value = rx_data_reg; rx_st_reg &= ~SERP_RX_ST_REG_READY_m; + update_rx_irq(); } else { value = 0; } + rx_queue_check(); break; case SERP_TX_ST_REG_o: value = tx_st_reg | SERP_TX_ST_REG_READY_m; @@ -124,3 +135,27 @@ std::uint32_t SerialPort::rword(std::uint32_t address, bool debug_access) const return value; } + +void SerialPort::update_rx_irq() const { + bool active = !!(rx_st_reg & SERP_RX_ST_REG_IE_m); + active &= !!(rx_st_reg & SERP_RX_ST_REG_READY_m); + if (active != rx_irq_active) { + rx_irq_active = active; + emit signal_interrupt(rx_irq_level, active); + } +} + +void SerialPort::update_tx_irq() const { + bool active = !!(tx_st_reg & SERP_TX_ST_REG_IE_m); + active &= !!(tx_st_reg & SERP_TX_ST_REG_READY_m); + if (active != tx_irq_active) { + tx_irq_active = active; + emit signal_interrupt(tx_irq_level, active); + } +} + +void SerialPort::rx_queue_check() const { + if (rx_st_reg & SERP_RX_ST_REG_IE_m) + pool_rx_byte(); + update_rx_irq(); +} diff --git a/qtmips_machine/serialport.h b/qtmips_machine/serialport.h index 5262720..0f469b7 100644 --- a/qtmips_machine/serialport.h +++ b/qtmips_machine/serialport.h @@ -55,15 +55,25 @@ signals: void rx_byte_pool(int fd, unsigned int &data, bool &available) const; void write_notification(std::uint32_t address, std::uint32_t value); void read_notification(std::uint32_t address, std::uint32_t *value) const; + void signal_interrupt(uint irq_level, bool active) const; + +public slots: + void rx_queue_check() const; public: bool wword(std::uint32_t address, std::uint32_t value); std::uint32_t rword(std::uint32_t address, bool debug_access = false) const; private: void pool_rx_byte() const; + void update_rx_irq() const; + void update_tx_irq() const; mutable std::uint32_t rx_st_reg; mutable std::uint32_t rx_data_reg; std::uint32_t tx_st_reg; + std::uint8_t tx_irq_level; + std::uint8_t rx_irq_level; + mutable bool tx_irq_active; + mutable bool rx_irq_active; }; } |