bool Processor::Network::WriteRegister(LFID fid, RemoteRegType kind, const RegAddr& raddr, const RegValue& value) { RegAddr addr = m_allocator.GetRemoteRegisterAddress(fid, kind, raddr); if (addr != INVALID_REG) { // Write it if (!m_regFile.p_asyncW.Write(addr)) { DeadlockWrite("Unable to acquire port to write register response to %s", addr.str().c_str()); return false; } if (!m_regFile.WriteRegister(addr, value, false)) { DeadlockWrite("Unable to write register response to %s", addr.str().c_str()); return false; } DebugSimWrite("F%u write %s register %s (physical %s) <- %s", (unsigned)fid, GetRemoteRegisterTypeString(kind), raddr.str().c_str(), addr.str().c_str(), value.str(addr.type).c_str() ); } return true; }
bool RegisterFile::CheckFPUOutputAvailability(RegAddr addr) { if (!p_asyncW.Write(addr)) { DeadlockWrite("Unable to acquire port to write back to %s", addr.str().c_str()); return false; } // Read the old value RegValue value; if (!ReadRegister(addr, value)) { DeadlockWrite("Unable to read register %s", addr.str().c_str()); return false; } if (value.m_state != RST_PENDING && value.m_state != RST_WAITING) { // We're too fast, wait! DeadlockWrite("FP operation completed before register %s was cleared", addr.str().c_str()); return false; } return true; }
bool FPU::OnCompletion(unsigned int unit, const Result& res) const { const CycleNo now = GetKernel()->GetActiveClock()->GetCycleNo(); Source *source = m_sources[res.source]; if (source->m_last_write == now && source->m_last_unit != unit) { DeadlockWrite("Unable to write back result because another FPU pipe already wrote back this cycle"); return false; } source->m_last_write = now; source->m_last_unit = unit; // Calculate the address of this register RegAddr addr = res.address; addr.index += res.index; if (!source->client->CheckFPUOutputAvailability(addr)) { DeadlockWrite("Client not ready to accept result"); return false; } // Write new value unsigned int index = res.index; #ifdef ARCH_BIG_ENDIAN const unsigned int size = res.size / sizeof(Integer); index = size - 1 - index; #endif RegValue value; value.m_state = RST_FULL; value.m_float.integer = (Integer)(res.value.toint(res.size) >> (sizeof(Integer) * 8 * index)); if (!source->client->WriteFPUResult(addr, value)) { DeadlockWrite("Unable to write result to %s", addr.str().c_str()); return false; } DebugFPUWrite("unit %u completed %s %s <- %s", (unsigned)unit, source->client->GetName().c_str(), addr.str().c_str(), value.str(addr.type).c_str()); return true; }
bool Processor::Network::ReadRegister(LFID fid, RemoteRegType kind, const RegAddr& raddr, RegValue& value) { const RegAddr addr = m_allocator.GetRemoteRegisterAddress(fid, kind, raddr); assert(addr != INVALID_REG); // The thread of the register has been allocated, read it if (!m_regFile.p_asyncR.Read()) { return false; } if (!m_regFile.ReadRegister(addr, value)) { return false; } if (value.m_state != RST_FULL) { /* It's possible that the last shared in a family hasn't been written. Print a warning, and return a dummy value. */ DebugProgWrite("F%u request for unwritten %s register %s (physical %s)", (unsigned)fid, GetRemoteRegisterTypeString(kind), raddr.str().c_str(), addr.str().c_str() ); value.m_state = RST_FULL; switch (addr.type) { case RT_FLOAT: value.m_float.fromfloat(0.0f); break; case RT_INTEGER: value.m_integer = 0; break; default: assert(0); // should not be here } } else { DebugSimWrite("F%u read %s register %s (physical %s) -> %s", (unsigned)fid, GetRemoteRegisterTypeString(kind), raddr.str().c_str(), addr.str().c_str(), value.str(addr.type).c_str() ); } return true; }
bool Processor::IOInterface::WaitForNotification(IONotificationChannelID dev, const RegAddr& writeback) { if (!m_nmux.SetWriteBackAddress(dev, writeback)) { DeadlockWrite("Unable to set the writeback address %s for wait on channel %u", writeback.str().c_str(), (unsigned)dev); return false; } return true; }
bool RegisterFile::WriteRegister(const RegAddr& addr, const RegValue& data, bool from_memory) { auto& regs = m_files[addr.type]; auto sz = m_sizes[addr.type]; if (addr.index >= sz) { throw SimulationException("A component attempted to write to a non-existing register", *this); } assert(data.m_state == RST_EMPTY || data.m_state == RST_PENDING || data.m_state == RST_WAITING || data.m_state == RST_FULL); if (data.m_state == RST_EMPTY || data.m_state == RST_PENDING) { assert(data.m_waiting.head == INVALID_TID); } auto& value = regs[addr.index]; if (value.m_state != RST_FULL) { if (value.m_state == RST_WAITING && data.m_state == RST_EMPTY) { throw exceptf<SimulationException>(*this, "Invalid reset of %s: %s becomes %s", addr.str().c_str(), value.str(addr.type).c_str(), data.str(addr.type).c_str()); } if (value.m_memory.size != 0) { // Check that the memory information isn't changed if (data.m_state == RST_FULL || data.m_memory.fid != value.m_memory.fid || data.m_memory.offset != value.m_memory.offset || data.m_memory.size != value.m_memory.size || data.m_memory.sign_extend != value.m_memory.sign_extend || data.m_memory.next != value.m_memory.next) { if (!from_memory) { // Only the memory can change memory-pending registers throw exceptf<SimulationException>(*this, "Invalid reset of pending load %s: %s becomes %s", addr.str().c_str(), value.str(addr.type).c_str(), data.str(addr.type).c_str()); } } } if (data.m_state == RST_FULL) { if (value.m_state == RST_WAITING && value.m_waiting.head != INVALID_TID) { // This write caused a reschedule auto& alloc = GetDRISC().GetAllocator(); if (!alloc.ActivateThreads(value.m_waiting)) { DeadlockWrite("Unable to wake up threads after write of %s: %s becomes %s", addr.str().c_str(), value.str(addr.type).c_str(), data.str(addr.type).c_str()); return false; } } } } COMMIT { #ifndef NDEBUG // Paranoid sanity check: // We cannot have multiple writes to same register in a cycle for (unsigned int i = 0; i < m_nUpdates; ++i) { assert(m_updates[i].first != addr); } #endif // Queue the update assert(m_nUpdates < MAX_UPDATES); m_updates[m_nUpdates] = make_pair(addr, data); if (m_nUpdates == 0) { RegisterUpdate(); } m_nUpdates++; } return true; }
Processor::Pipeline::PipeAction Processor::Pipeline::WritebackStage::OnCycle() { int writebackOffset = m_writebackOffset; int size = -1; bool allow_reschedule = true; bool suspend = (m_input.suspend != SUSPEND_NONE); if (m_stall) { // We need to stall this cycle without doing *anything*. // Since we stall, we never commit. if (!IsAcquiring()) { m_stall = false; } return PIPE_STALL; } if (m_input.Rrc.type != RemoteMessage::MSG_NONE) { // Network activity if (!m_network.SendMessage(m_input.Rrc)) { DeadlockWrite("Unable to send network message"); return PIPE_STALL; } DebugPipeWrite("F%u/T%u(%llu) %s sent network message %s", (unsigned)m_input.fid, (unsigned)m_input.tid, (unsigned long long)m_input.logical_index, m_input.pc_sym, m_input.Rrc.str().c_str()); } if (m_input.Rcv.m_state != RST_INVALID) { // We have something to write back assert(m_input.Rcv.m_size % sizeof(Integer) == 0); size = m_input.Rcv.m_size / sizeof(Integer); if (writebackOffset == -1) { // New data write writebackOffset = 0; } if (writebackOffset < size) { // Take data from input RegValue value = MAKE_EMPTY_REG(); value.m_state = m_input.Rcv.m_state; switch (value.m_state) { case RST_PENDING: case RST_WAITING: case RST_EMPTY: if (writebackOffset == 0) { // Store the thread and memory information in the first register only value.m_waiting = m_input.Rcv.m_waiting; value.m_memory = m_input.Rcv.m_memory; } break; case RST_FULL: { // Compose register value unsigned int index = writebackOffset; #ifdef ARCH_BIG_ENDIAN index = size - 1 - index; #endif const unsigned int shift = index * 8 * sizeof(Integer); switch (m_input.Rc.type) { case RT_INTEGER: value.m_integer = (Integer)(m_input.Rcv.m_integer.get(m_input.Rcv.m_size) >> shift); break; case RT_FLOAT: value.m_float.integer = (Integer)(m_input.Rcv.m_float.toint(m_input.Rcv.m_size) >> shift); break; } break; } default: // These states are never written from the pipeline UNREACHABLE; } if (m_input.Rc.valid()) { // Get the address of the register we're writing. const RegAddr addr = MAKE_REGADDR(m_input.Rc.type, m_input.Rc.index + writebackOffset); // We have something to write back if (!m_regFile.p_pipelineW.Write(addr)) { DeadlockWrite("F%u/T%u(%llu) %s unable to acquire write port on RF", (unsigned)m_input.fid, (unsigned)m_input.tid, (unsigned long long)m_input.logical_index, m_input.pc_sym); return PIPE_STALL; } // Read the old value RegValue old_value; if (!m_regFile.ReadRegister(addr, old_value)) { DeadlockWrite("F%u/T%u(%llu) %s unable to read prev %s", (unsigned)m_input.fid, (unsigned)m_input.tid, (unsigned long long)m_input.logical_index, m_input.pc_sym, addr.str().c_str()); return PIPE_STALL; } if (value.m_state == RST_WAITING) { assert(suspend == true); if (old_value.m_state == RST_FULL) { // The data we wanted to wait for has returned before we could write the register. // Just reschedule the thread. suspend = false; value.m_state = RST_INVALID; DebugPipeWrite("F%u/T%u(%llu) %s data arrived in %s since read, rescheduling", (unsigned)m_input.fid, (unsigned)m_input.tid, (unsigned long long)m_input.logical_index, m_input.pc_sym, addr.str().c_str()); } else { assert(value.m_waiting.head == m_input.tid); // The Read Stage will have setup the register to // link this thread into the register's thread waiting list if (old_value.m_state == RST_WAITING && old_value.m_waiting.head != INVALID_TID) { // Not the first thread waiting on the register // Update the tail assert(value.m_waiting.tail == old_value.m_waiting.tail); } else { assert(value.m_waiting.tail == m_input.tid); } COMMIT { // We're suspending because we're waiting on a non-full register. // Since we support multiple threads waiting on a register, update the // next field in the thread table to the next waiting thread. m_threadTable[m_input.tid].next = old_value.m_waiting.head; } } } else if (value.m_state == RST_PENDING && old_value.m_state == RST_WAITING)
bool FPU::OnCompletion(unsigned int unit, const Result& res) const { const CycleNo now = GetCycleNo(); Source *source = m_sources[res.source]; if (source->last_write == now && source->last_unit != unit) { DeadlockWrite("Unable to write back result because another FPU pipe already wrote back this cycle"); return false; } source->last_write = now; source->last_unit = unit; // Calculate the address of this register RegAddr addr = res.address; addr.index += res.index; if (!source->regfile->p_asyncW.Write(addr)) { DeadlockWrite("Unable to acquire port to write back to %s", addr.str().c_str()); return false; } // Read the old value RegValue value; if (!source->regfile->ReadRegister(addr, value)) { DeadlockWrite("Unable to read register %s", addr.str().c_str()); return false; } if (value.m_state != RST_PENDING && value.m_state != RST_WAITING) { // We're too fast, wait! DeadlockWrite("FP operation completed before register %s was cleared", addr.str().c_str()); return false; } // Write new value unsigned int index = res.index; #ifdef ARCH_BIG_ENDIAN const unsigned int size = res.size / sizeof(Integer); index = size - 1 - index; #endif value.m_state = RST_FULL; value.m_float.integer = (Integer)(res.value.toint(res.size) >> (sizeof(Integer) * 8 * index)); if (!source->regfile->WriteRegister(addr, value, false)) { DeadlockWrite("Unable to write register %s", addr.str().c_str()); return false; } DebugFPUWrite("unit %u completed %s %s <- %s", (unsigned)unit, source->regfile->GetParent()->GetFQN().c_str(), addr.str().c_str(), value.str(addr.type).c_str()); return true; }