aboutsummaryrefslogtreecommitdiff
path: root/qtmips_machine
diff options
context:
space:
mode:
authorPavel Pisa <pisa@cmp.felk.cvut.cz>2019-03-04 15:30:11 +0100
committerPavel Pisa <pisa@cmp.felk.cvut.cz>2019-03-04 15:30:11 +0100
commit1dc09ab41ae703a1f15be87cacab5e842cf1a09d (patch)
tree392908ad6642e1ad5f9afa8c21998665a83534af /qtmips_machine
parentcd3a4b268a9b3d4d5436fee5de7967adb6f6e170 (diff)
downloadqtmips-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.cpp30
-rw-r--r--qtmips_machine/cop0state.h13
-rw-r--r--qtmips_machine/core.cpp14
-rw-r--r--qtmips_machine/qtmipsmachine.cpp4
-rw-r--r--qtmips_machine/qtmipsmachine.h1
-rw-r--r--qtmips_machine/serialport.cpp41
-rw-r--r--qtmips_machine/serialport.h10
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;
};
}